/react-es6-webpack-setup

react es6 webpack setup with livereload and implement a state convertible button by reactjs

Primary LanguageJavaScriptMIT LicenseMIT

react-es6-webpack-setup

在使用webpack建立ReactJS專案之前,要先安裝nodeJS,安裝完後
輸入指令

node -v

以及

npm -v

可查看現在使用的版本
我使用的版本是
node v6.2.1
npm v3.10.3
確定npm安裝完成後即可開始接下來的步驟

React JS+webpack環境設定&環境測試

以下步驟參考 build-a-hn-front-page

安裝webpack

使用指令

npm install webpack@1.13.1 --save-dev

webpack安裝進專案資料夾中
--save-dev代表將webpack安裝儲存於專案目錄下

這句指令可簡寫成

npm i webpack@1.13.1 -D

npm install可簡寫成npm i
--save-dev可以簡寫成-D
npm install相關指令簡寫可參考 install | npm Document
--save-dev指令代表安裝在哪參考 WEBPACK入門教學筆記

資料夾與檔案結構

按照以下結構建立檔案資料夾來測試環境設定:

  • app/
    • main.js
  • build/
    • index.html
    • (bundle.js) (此檔案不用建立,使用 webpack指令後會自動生成)
  • package.json
  • webpack.config.js
建立wepack.config.js , 填入以下程式碼:
/* webpack.config.js */
var path = require('path');

module.exports = {
    entry: [path.resolve(__dirname, 'app/main.js')],
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'bundle.js'
    }
};

我們先測試一下

建立 app/main.js , 填入以下程式碼:
document.write('It works');
建立 build/index.html , 填入以下程式碼:
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
</head>

<body>
    <script src="bundle.js"></script>
</body>

</html>

html中script tag引入了 bundle.js, 這是執行webpack指令後,將js打包之後輸出的檔案

輸入指令

webpack

用瀏覽器開啟build資料夾中的index.html,如果成功的話,會看到瀏覽器顯示 It works

設定package.json

在專案目錄下用指令

npm init

建立 package.json ,建立的過程中全按enter就好

修改package.json中scripts的值
{
...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "./node_modules/.bin/webpack"
  }
}

現在輸入指令

npm run build

就等同於輸入指令./node_modules/.bin/webpack
為什麼不直接用webpack而是前面要用./node_modules/.bin/webpack呢?
原因可參考 cleaner installation for developers (Evernote)cleaner installation for developers (影片 REACT JS TUTORIAL #1) 8:51 ~ 10:08

安裝需要的模組

安裝 React

npm i react@15.2.1 react-dom@15.2.1 -D

安装 Babel 的 loader 以支持 ES6 語法

npm i babel-core@6.10.4 babel-loader@6.2.4 babel-preset-es2015@6.9.0 babel-preset-react@6.11.1 -D
修改webpack.config.js來使用安裝的loader
/* webpack.config.js */
var path = require('path');

module.exports = {
    entry: [path.resolve(__dirname, 'app/main.js')],
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'bundle.js'
    },
    module: {
        loaders: [
        {
            test: /\.js$/,
            loaders: ['babel?presets[]=es2015,presets[]=react']
        }
        ]
    }
};

此處test的語法為正規表示法(Regular Expression)
loaders相關的語法參考
stackoverflow: Webpack - Error: Cannot define 'query' and multiple loaders in loaders list
測試一下開發環境是否建立完成

修改app/main.js
/* main.js */
import React from 'react';
import { render } from 'react-dom';

export default class Main extends React.Component {
    render() {
        return (
            <div>Hello World</div>      
        );
    }
};

var myElement = document.getElementById('content');

render(<Main />, myElement);

Hello World被放進id為content的DOM元素中
所以需要增加元件到index.html

build/index.html
...
<body>
  <div id="content"></div>
  <script src="./bundle.js"></script>  
</body>
...

經過以上步驟,我們已將所有的環境設定建立完成

用ReactJS+webpack實作按鈕轉換state功能

此專案程式碼參考 Rhadow's Tech Note

資料夾與檔案結構

按照以下結構建立檔案資料夾來建立專案:

  • app/
    • main.js
    • TestOne.js
    • TestTwo.js
  • build/
    • index.html
    • (bundle.js) (此檔案不用建立,使用 webpack指令後會自動生成)
  • package.json
  • webpack.config.js
新增app/TestOne.js,並輸入以下程式碼
/* TestOne.js */
import React from 'react';

export default class TestOne extends React.Component {
    render() {
        return (
            <div>Hello I am TestOne Component</div>
        );
    }
};
新增app/TestTwo.js,並輸入以下程式碼
/* TestTwo.js */
import React from 'react';

export default class TestTwo extends React.Component {
    render() {
        return (
            <h1>Hello I am TestTwo Component</h1>
        );
    }
};

import和export為es6的語法
用法可參考 module (Evernote) 或者是 module (影片 Javascript ES6 Cheatsheet #2) 13:33 ~ 17:37
class和extend也是es6的語法
用法可參考 class (Evernote) 或者是 class (影片 Javascript ES6 Cheatsheet #2) 4:32 ~ 7:11
export default用法參考 ES6 Modules (Evernote)

React es6的寫法和之前版本寫法的差異
可參考 React.createClass versus extends React.Component

修改app/main.js
/* main.js */
import React from 'react';
import { render } from 'react-dom';
import TestOne from './TestOne.js';
import TestTwo from './TestTwo.js';

export default class Main extends React.Component {
    constructor() {
        super();
        this.state = {
            isActive: false
        };
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick () {
        var active = !this.state.isActive;
        this.setState({ isActive: active });
    }

    render() {
        return (
            <div>
                <input type="button" onClick={this.handleClick} value="Press Me!"/>
                {this.state.isActive ? <TestTwo /> : <TestOne />}
            </div>      
        );
    }
};

var myElement = document.getElementById('content');

render(<Main />, myElement);

{ render } 為es6 destructuring assignment的寫法
詳細用法可參考 desturcturing assignment (Evernote)destructuring assignment (影片 Javascript ES6 Cheatsheet) 1:35 ~ 10:51
關於component state的寫法
可參考 Best Practices for Component State in React.js
這邊要注意一點
React的createClass方法(舊寫法)的this的context一律對應到class
但React class extends React.Component(es6,新寫法)中method裡的this的context會改變
所以要記得在後面加bind(this)

最後用瀏覽器開啟 build/index.html
無打字錯誤的話,應該能看到功能執行

使用 webpack-dev-server 實現 LiveReload

以下內容參考 Rhadow's Tech Note

每次我們修改程式碼,想要看到執行結果時,都必須要輸入指令 npm run build 還要到瀏覽器重新整理,非常麻煩
webpack-dev-server 讓我們可以省去這個麻煩
首先,先安裝webpack-dev-server,執行指令

npm i webpack-dev-server@1.14.1 -D
package.json 內加入一個新 dev 的指令:
{
...
  "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1",
      "build": "./node_modules/.bin/webpack",
      "dev": "./node_modules/.bin/webpack-dev-server --devtool eval --progress --colors --inline --hot --content-base build"
  },
...
}

dev內的指令解釋如下:

webpack-dev-server 會在 localhost:8080 建立起專案的 server
--devtool eval 會顯示出發生錯誤的行數與檔案名稱
--progress 會顯示出打包的過程
--colors 會幫 webpack 顯示的訊息加入顏色
--inline --hot 參考 inline (Evernote)inline (影片REACT JS TUTORIAL #1) 8:01 ~ 8:50
--content-base build 指向專案最終輸出的資料夾build

再來到 webpack.config.js 的 entry 屬性內加入 'webpack/hot/dev-server'如下:
var path = require('path');

module.exports = {
    entry: ['webpack/hot/dev-server', path.resolve(__dirname, 'app/main.js')],
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'bundle.js'
    },
    module: {
        loaders: [
        {
            test: /\.js$/,
            loaders: ['babel?presets[]=es2015,presets[]=react']
        }
        ]
    }
};

這樣就完成 LiveReload 的功能囉
執行 npm run dev 後開啟瀏覽器到 http://localhost:8080/
檢查程式是否正確執行
也可試著改變程式碼,觀察瀏覽器畫面是否有跟著變動。

加入 react-hot-loader

以下內容參考 Rhadow's Tech Note

如果有跟著以上每一步做的話
程式跑起來後應該會有一個按鈕和 “Hello, I’m TestOne Component” 的字出現在畫面上。
重複點擊按鈕,”Hello, I’m TestOne Component” 和 “Hello, I’m TestTwo Component” 會輪流出現在畫面上。

小實驗

現在請將畫面切到顯示 “Hello, I’m TestTwo Component” ,並到 TestTwo.js 將 “Hello, I’m TestTwo Component” 改為 “Hello” 後儲存。
完成後,會發現瀏覽器畫面會回到最初的 “Hello, I’m TestOne Component”,這是因為 LiveReload 會重新整理畫面並將 React 元件的 state 重設回到初始值。

react-hot-loader 的功用就是在不清除 state 的狀態下更新畫面
讓我們趕快來看看該如何達成這個效果吧。

安裝 react-hot-loader
npm i react-hot-loader@1.3.0 -D
修改 webpack.config.js 內的 entry 屬性如下:
...
module.exports = {
  entry: [
      'webpack-dev-server/client?http://localhost:8080',
      'webpack/hot/only-dev-server',
      path.resolve(__dirname, 'app/main.js')
  ],
...
}

再來修改 module 屬性如下:

...
module.exports = {
...
module: {
    loaders: [
    {
        test: /\.js$/,
        loaders:  ['react-hot', 'babel?presets[]=es2015,presets[]=react'],
        include: path.join(__dirname, 'app')
    }
    ]
},

要特別注意的地方是一定要加入 include: path.join(__dirname, 'app'),不然 webpack 會把 node_modules 內的 js 檔都透過 react-hot-loader 跑一遍,會因此導致 Cannot read property ‘NODE_ENV’ of undefined 錯誤造成程式無法正常運作。

最後一步,在 webpack.config.js 內加入以下屬性,別忘了要先宣告 var webpack = require('webpack');:
...
var webpack = require('webpack');

module.exports = {
...
  plugins: [
      //new webpack.HotModuleReplacementPlugin(),
      new webpack.NoErrorsPlugin()
  ]
}

這邊的 webpack.NoErrorsPlugin() 是選擇性的,主要的功能是當更改完的程式碼有語法錯誤時不要重新整理。 當錯誤修好後,畫面會自動重新整理。

new webpack.HotModuleReplacementPlugin() 則是當不使用 webpack-dev-server 的 inline-mode 時才需要加入。本例是使用 inline-mode,你可以試著把註解掉的部分加回程式裡,當元件在做更新時,會出現 Uncaught RangeError: Maximum call stack size exceeded 的錯誤訊息。inline-mode 詳情請參考 webpack-dev-server 說明。

現在執行 npm run dev 後再一次做本段落最開始的小實驗,就會發現更改程式碼後,畫面不會再回到首頁了。

功能

顯示Hello I am TestOne Component
按下Click me按鈕後
變成顯示粗體的Hello I am TestOne Component