這篇教學介紹React專案的基本webpack設定。包含支援ES6需要的Babel,程式碼風格檢查的ESLint和開發工具webpack-dev-server

目錄

專案資料夾結構 Overview

假設我們專案的資料夾結構如下:

├── package.json
├── src
│   ├── actions/
│   ├── containers/
│   ├── components/
│   ├── reducers/
│   ├── store/
│   ├── client.jsx
│   ├── routes.jsx
└── webpack.client.config.js

src/包含所有client-side的程式碼,其中src/client.jsx是我們app的進入點。而webpack設定放在webpack.client.config.js裡。

Webpack基本設定

安裝webpack:

npm install -D webpack

在根目錄底下編輯webpack.client.config.js

// webpack.client.config.js
module.exports = {
  entry: {
    client: './src/client'
  },
  output: {
    path: './bin',
    filename: '[name].js'
  },
  resolve: {
    extensions: ["", ".js", '.jsx']
  },
  devtool: "source-map"
}
  • entry是js的進入點,client是bundle的名稱,而./src/client表示當bundle被載入時會載入./src/client的module,而webpack會從這個進入點開始編譯。
  • output.filename: '[name].js'設定成編譯出來的bundle會根據entry的key值命名,也就是client.js
  • resolve.extension可以讓我們import jsjsx時可以省略附檔名。
  • devtool則是source map的設定,基於速度快慢和訊息的詳盡程度,選一個自己喜歡的即可。

Babel設定

Babel是用來將ES6和react的jsx語法轉譯成ES5的工具。

安裝Babel:

npm install -D babel-core babel-loader

安裝ES6和react的presets:

npm install -D babel-preset-es2015 babel-preset-react

在專案根目錄底下設定.babelrc

//.babelrc
{
  "presets": ["es2015", "react"]
}

設定使用babel-loader載入jsjsx

// webpack.client.config.js
module.exports = {
  ...
  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        loader: 'babel',
        include: /src/
      }
    ]
  },
  ...
}

ESLint設定

ESLint可以用靜態分析的方法抓出程式中的語法錯誤,以及容易出錯的寫法,讓開發過程中減少很多低級失誤。

安裝ESLint和loader:

npm install -D eslint eslint-loader

初始設定(推薦用問答的方式,產生的設定會在.eslintrc.js):

./node_modules/.bin/eslint --init

在webpack config中加入eslint-loader (要放在最後面,才能檢查到react/es6語法):

module.exports = {
  ...
  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        loaders: ['babel', 'eslint'],
        include: /src/
      }
    ]
  },
  ...
}

加入React rules(避免unused React錯誤,以及jsx的語法檢查):

// .eslintrc.js
"extends": [
  ...
  "plugin:react/recommended"
]

eslint-plugin-import

當你import某個module時不小心打錯字,只會默默的import成undefined。舉例來說,這是我曾經犯過的一個錯:

import {Provider} from 'redux' // Should be from 'react-redux'

JS沒給我任何警告或錯誤,只在runtime時狂噴莫名其妙的error,害我浪費超多時間QQ。後來找到eslint-plugin-import,他可以幫你檢查import的正確性(雖然實際使用起來似乎不是百分之百都抓得到錯誤)。

加入eslint-plugin-import外掛:

npm install -D eslint-plugin-import eslint-import-resolver-webpack

新增設定:

// .eslintrc.js
module.exports = {
  ...
  "extends": [
    ...
    "plugin:import/errors",
    "plugin:import/warnings"
  ],
  plugins: [
    ...
    "import"
  ],
  "settings": {
    "import/resolver": {
      "webpack": {
        "config": {
          "resolve": {
            "extensions": ['', '.js', '.jsx']
          }
        }
      }
    }
  }
}

webpack-dev-server

webpack-dev-server是個express server,主要的功能負責serve bundle。他能夠做到當檔案修改時會重新編譯bundle,並且讓網頁自動refresh。更進階一點,還能在網頁不更動的情況下作Hot Module Replacement,讓開發的工作更加輕鬆。

webpack-dev-server會把bundle放在memory裡面,不會產生實體的檔案。而靜態檔案預設會從當前目錄serve。

安裝:

npm install -D webpack-dev-server

首先在webpack.client.config.js新增devServer的設定:

module.exports = {
  ...
  output: {
    ...
    publicPath: '/static/' // bundle is served at this path
  },
  devServer: {
    historyApiFallback: true,
    inline: true
  }
}
  • output.publicPath的設定表示bundle可以在<domain>/static/client.js的路徑被存取。
  • 設定devServerinline讓瀏覽器可以自動refresh。historyApiFallback是讓single page application第一次進入的路徑不管為何都能讓routing正常運作。

我們還需要一個靜態的index.html。因為靜態檔案預設從當前目錄serve,我們把index.html放在根目錄底下:

<!doctype html>
<html>
  <head>
    <title>Redux Universal Example</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="/static/client.js"></script>
  </body>
</html>

注意<script src="/static/client.js"></script>相對路徑同publicPath

最後加上npm script:

// package.json
"scripts": {
  "start": "webpack-dev-server --config webpack.client.config.js"
},

執行:

npm start

Conclusion

這樣專案就設定完成了,可以使用ES6及React的語法,包含ESLint檢查,以及支援瀏覽器自動refresh的webpack-dev-server

完整的範例可以參考react-universal-example

Reference

Webpack Official

Survivejs: 很詳盡的教學,比官方文件多很多範例

Webpack your bags: 一篇講解code splitting, chunk和css滿清楚的文章