這篇會講


目錄

  1. 安裝 Webpack
  2. 為什麼要用 Webpack?
  3. 環境配置
    1. webpack.config.js
      1. entry
      2. output
        1. resolve
        2. module
      3. devtool
      4. plugins
    2. 推薦配置
      1. loaders
      2. plugins
      3. 兼容 IE
      4. webpack.config.js 配置範例
  4. Webpack + Express
    1. 什麼是 Express?
    2. 什麼是 webpack-dev-middleware & webpack-dev-server?
    3. 安裝 Express & webpack-dev-middleware
    4. 使用配置
    5. Hot Module Replacement
      1. 這是神馬?
      2. 安裝
      3. 配置
  5. 牛刀小試
    1. index.html
    2. main.jsx
    3. webpack 建置
  6. 總結
    1. 環境配置懶人包

安裝 Webpack

存於單一專案:
npm install --save-dev webpack

全域,存於本機:
npm install -g webpack

按照需求擇一安裝即可,等待安裝的同時,來說說什麼是 Webpack。

為什麼要用 Webpack?

  • 兼容 CommonJS & AMD & ES6 模組規範
  • Bundle 效率高
  • JS 擴充語法的編譯自動化 (JSX, Coffee Script, TypeScript…)
  • 編譯 sass, less
  • 將資源 (css, img, font…) 包入 JS 內
  • JS 程式碼分散封裝
  • 可用的擴充 plugin 很多

環境配置

webpack.config.js

首先,在 NPM 專案的根目錄建立一個名叫 webpack.config.js 的檔案,並輸入以下內容:

1
2
3
4
5
var path = require('path');
var webpack = require('webpack');
module.exports = {
};

所有設定將打在 module.exports 中輸出。

entry

說明:Webpack 會將每個 entry 文件編譯打包。當然,其中 require 的文件 (js, css, img) 會一起被打包進來。
格式:字串或字串陣列 (一個檔案對應一個字串,為 JS 檔)

EX:

1
2
3
entry: [
'./main.jsx'
]

output

說明:打包後生成的檔案路徑。
格式:物件
屬性:

  • path
    說明:打包生成的目錄
    格式:字串
  • filename
    說明:生成的 js 檔名
    格式:字串
  • publicPath
    說明:CSS 打包時修改的引用檔案路徑
    格式:字串

EX:

1
2
3
4
5
6
output: {
// path.join => 兩個參數,依當前的作業系統幫你在中間加 '/' 或 '\',然後串接
path: path.join(__dirname, 'dist'),
filename: 'compiled.js',
publicPath: '/'
}

resolve

說明:require 相關設置。
格式:物件
屬性:

  • root
    說明:require 的根目錄 (模組的引用不受影響)
    格式:字串或字串陣列
  • extensions
    說明:require 可省略的副檔名
    格式:字串或字串陣列
  • ailas
    說明:屬性對應的值會形成 ailas 對應。ailas: { a:'b' } => require('a') 同於 require('b')
    格式:物件

EX:

1
2
3
4
5
6
resolve: {
// path.resolve 可以傳入任意數量的字串,會將他們以類似 cd 的方式一一執行,並回傳最後的絕對路徑
root: [path.resolve(__dirname, 'src')],
extensions: ['', '.js', '.jsx','css', '.scss'],
ailas: { a:'b' }
}

module

格式:物件
屬性:

  • loaders
    說明:entry 及 require 到的檔都會依此轉換成 JS,anything to JS
    格式:物件陣列(由下往上依序轉換)
    屬性:
    • test
      說明:指定目標檔案的檔名
      格式:正則表達式
    • loader
      說明:指定使用的 loader
      格式:字串或字串陣列 (loader 語法)(由右往左依序轉換)
    • include
      說明:白名單,只處理的目錄
      格式:字串或字串陣列
    • exclude
      說明:黑名單,忽略、不處理的目錄
      格式:字串或字串陣列

EX:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module: {
loaders: [{
// '/' 是 JS 正則表達式標記,'.' 是正則表達式關鍵字,所以前面要加個 '\' 讓正則表達式以字元方式處理,'|' 是 '或' 的意思,'$' 是字串結束符號
// 整體意思是找檔名末尾是 .js 或 .jsx 的
test: /\.(js|jsx)$/,
// '-loader' 可省略,即 'babel'
loader: 'babel-loader',
// node_modules 的 JS 檔必定是瀏覽器原本就能吃的 ES5,不需要經過編譯,所以忽略以增加效率和避免錯誤
exclude: /node_modules/
}, {
test: /\.css$/,
// '-loader' 可省略,'!' 表示 loader 串聯順序(由右往左依序轉換),'?' 表示傳送請求參數(類似 get)來進階設定
// loader 串聯亦可用陣列表示:
// loader: [ 'style-loader' , 'css-loader?sourceMap' ]
loader: 'style-loader!css-loader?sourceMap'
}]
}

devtool

說明:設置 eval 或 SourceMap 屬性,debug 用
格式:字串
種類:

  • 'eval'
  • 'source-map'
  • 'hidden-source-map'
  • 'inline-source-map'
  • 'eval-source-map'
  • 'cheap-source-map'
  • 'cheap-module-source-map'

可以混著用,個別的介紹請看這裡我懶的打字
EX:

1
devtool: 'cheap-module-eval-source-map'

plugins

說明:插件配置
格式:物件或物件陣列,通常以 new webpack.XXXPlugin() 產生物件

EX:

1
2
3
4
5
6
7
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.ProvidePlugin({
React: 'react',
ReactDOM:'react-dom'
})
]

嘛,大致上是這樣,如果對 Webpack 配置有興趣可看這裡

推薦配置

loaders

懶人包,全裝下去 94 狂
npm install --save-dev babel-loader style-loader css-loader sass-loader less-loader url-loader

  • babel-loader
    功能:自動編譯 JSX 或 JS 檔 (require 可載入 JSX 了)
    安裝:npm install --save-dev babel-loader
    示範:

    1
    2
    3
    4
    5
    {
    test: /\.(js|jsx)$/,
    loader: 'babel',
    exclude: /node_modules/
    }
  • style-loader
    功能:自動將 CSS 檔的內容插入到頁面有 require CSS 的地方,並用 style 包住 (require 可載入 CSS 了)
    安裝:npm install --save-dev style-loader
    示範:見下面 css-loader 的示範

  • css-loader
    功能:自動處理 CSS 內的 url@import 的路徑轉換,可以傳入 sourceMap 參數以便 debug
    安裝:npm install --save-dev css-loader
    示範:

    1
    2
    3
    4
    {
    test: /\.css$/,
    loader: 'style!css?sourceMap'
    }
  • sass-loader
    功能:自動編譯 sass 檔成 CSS (require 可載入 sass 了),可以傳入 sourceMap 參數以便 debug
    安裝:npm install --save-dev sass-loader
    示範:

    1
    2
    3
    4
    {
    test: /\.scss$/,
    loader: 'style!css?sourceMap!sass?sourceMap'
    }
  • less-loader
    功能:自動編譯 less 檔成 CSS (require 可載入 less 了),可以傳入 sourceMap 參數以便 debug
    安裝:npm install --save-dev less-loader
    示範:

    1
    2
    3
    4
    {
    test: /\.less$/,
    loader: 'style!css?sourceMap!less?sourceMap'
    }
  • url-loader
    功能:自動將圖片轉成 Data URL
    安裝:npm install --save-dev url-loader
    示範:

    1
    2
    3
    4
    {
    test: /\.(jpe?g|JPE?G|png|PNG|gif|GIF|svg|SVG|woff|woff2|eot|ttf)(\?v=\d+\.\d+\.\d+)?$/,
    loader: 'url?limit=1024&name=[sha512:hash:base64:7].[ext]'
    }

plugins

  • HotModuleReplacementPlugin
    功能:Hot Module Replacement,詳細會在底下提到。
    示範:

    1
    new webpack.HotModuleReplacementPlugin()
  • ProvidePlugin
    功能:幫你 require,new webpack.ProvidePlugin({a: 'b'}) => 程式碼有找到 a 字串就載入 b module,就不用手動 require
    示範:

    1
    2
    3
    4
    5
    new webpack.ProvidePlugin({
    $: 'jquery',
    React: 'react',
    ReactDOM:'react-dom'
    })

兼容 IE

說到 IE,一堆工程師的頭都痛了。
eventsource-polyfill 可以讓 Webpack 兼容 IE!
修改 webpack.config.js 的 entry:

1
2
3
4
5
entry: [
// 一定要在前面
'eventsource-polyfill',
'./src/main.jsx',
],

webpack.config.js 配置範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: [
'eventsource-polyfill',
'./main.jsx'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'compiled.js',
publicPath: '/'
},
resolve: {
root: [
path.join(__dirname, 'src')
],
extensions: ['', '.js', '.jsx','css', '.scss']
},
module: {
loaders: [{
test: /\.(js|jsx)$/,
loader: 'babel',
exclude: /node_modules/
}, {
test: /\.css$/,
loader: 'style!css?sourceMap'
}, {
test: /\.scss$/,
loader: 'style!css?sourceMap!sass?sourceMap'
}, {
test: /\.(jpe?g|JPE?G|png|PNG|gif|GIF|svg|SVG|woff|woff2|eot|ttf)(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?limit=1024&name=[sha512:hash:base64:7].[ext]'
}]
},
devtool: 'cheap-module-eval-source-map',
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.ProvidePlugin({
$: 'jquery',
React: 'react',
ReactDOM:'react-dom'
})
]
};

Webpack + Express

什麼是 Express?

Express 是最小又靈活的 Node.js Web 應用程式架構,為 Web 與行動式應用程式提供一組健全的特性。

– 仁自 Express 官網

簡而言之,Express 是目前使用上最穩定且最廣泛的輕量級開發框架,而且是 Node.js 官方唯一推薦的框架。
因為 webpack 本身只是 bundle 工具,沒有 server 功能,需要一個 server。
然而,要將 webpack 跟 Express 結合,需要使用 webpack-dev-middlewarewebpack-dev-server不然只能同時開兩個 terminal,一個跑 webpack,一個跑 Express server

不想使用 Express 的話可以跳過

什麼是 webpack-dev-middleware & webpack-dev-server?

webpack-dev-middleware 可讓 webpack 被 Express app 或是其他可使用 middleware 的框架所使用。
webpack-dev-server 本身就是小型的 Express server,簡單來說整合了 Express 和 webpack-dev-middleware。
webpack-dev-server 方便架設,但彈性較少,無法跟現有的 Express app 結合,且只能用 Express。

所以我採用了 Express + webpack-dev-middleware。想用 webpack-dev-server 的話可以參考下這裡

安裝 Express & webpack-dev-middleware

npm install --save express
npm install --save-dev webpack-dev-middleware

使用配置

裝好之後要用 Express 寫個 server,套用 webpack-dev-middleware。
首先,在專案根目錄建一個 server.js (檔名隨意)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var path = require('path');
// 使用 express
var express = require('express');
var app = express();
// 使用 webpack
var webpack = require('webpack');
var config = require('./webpack.config');
var compiler = webpack(config);
// 將 webpack 傳入 webpack-dev-middleware 並套用至 app,同時傳入屬性,webpack 就可以被加載進來
app.use(require('webpack-dev-middleware')(compiler, {
noInfo: true,
publicPath: config.output.publicPath
}));
// 不管你打什麼都會載入 index.html 啦
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'index.html'));
});
// 監聽 8000 port,並顯示錯誤或成功
app.listen(8000, function(err) {
if (err) {
console.log(err);
return;
}
console.log('Listening at http://localhost:8000');
});

Hot Module Replacement

這是神馬?

中文稱為熱置換,意思是你修改任意一個檔案並儲存後就會自行編譯打包 (webpack –watch),然後更新瀏覽器畫面 (Browsersync),而且只會 reload 更新的一部份而不會全部 reload,全部 reload 效率低,且會導致狀態的遺失。

webpack –watch + Browsersync 還是無法避免全部 reload,無法做到 HMR,所以,我們得用 react-hot-loaderwebpack-hot-middleware

react-hot-loader 是 webpack-dev-server 的衍生套件,webpack-hot-middleware 則是 webpack-dev-middleware 的衍生套件。請依環境選裝。

安裝

二選一

  • npm install --save-dev react-hot-loader
  • npm install --save-dev webpack-hot-middleware

配置

  • react-hot-loader
    修改 webpack.config.js 的 entry:

    1
    2
    3
    4
    5
    6
    entry: [
    // 一定要在前面
    'webpack-dev-server/client?http://0.0.0.0:3000',
    'webpack/hot/only-dev-server',
    './main.jsx',
    ],
  • webpack-hot-middleware
    修改 webpack.config.js 的 entry:

    1
    2
    3
    4
    5
    entry: [
    // 一定要在前面
    'webpack-hot-middleware/client',
    './main.jsx',
    ],

並在 server.js 中載入 webpack-dev-middleware 的後面再加一段:

1
2
// 將 webpack 傳入 webpack-hot-middleware 並套用至 app,就可達到 HMR 的效果
app.use(require('webpack-hot-middleware')(compiler));

無論用哪個都別忘了在 webpack.config.dev 加 new webpack.HotModuleReplacementPlugin() 才有 HMR 的效果喔~

牛刀小試

Webpack 搞了這麼久,趕快體驗一下他帶來的方便吧!

不過接下來至少還要改兩個檔才能開始測試 webpack。
這邊接序第二篇的環境建置,忘記的可以回去看一下。

index.html

這個檔必須是你用的 server 能讀取到的檔案
(若你是用 Express + webpack-dev-middleware 並實作了 server.js,那應該可在 server.js 內看到 sendFile 的對象,像我剛剛是在 server.js 中打 index.html)

1
2
3
4
5
6
7
8
9
10
<!doctype html>
<html>
<head>
<title> C8763 </title>
</head>
<body>
<div id="root"></div>
<script src="./compiled.js"></script>
</body>
</html>

有沒有覺得 compiled.js 這路徑很熟悉?
沒錯,它就是 webpack.config.js 中 output 設定的 bundle 輸出路徑。也就是說,載入這個 js 就好啦!
第七行 div 是讓 JSX 插入用的,id 可以自取。

main.jsx

有沒有覺得 main.jsx 這路徑也很熟悉?
沒錯,它就是 webpack.config.js 中 entry 設定的 bundle 進入點。
可以開始寫 JSX 囉。
以下只是範例:

1
2
3
import ReactDOM from 'react-dom'
import Demo from './Demo.jsx'
ReactDOM.render(<Demo/>, document.getElementById('root'));

document.getElementById(‘root’) 的 id 必須和 index.html 中 div 的 id 一致。

webpack 建置

  • 如果你是用 Express + webpack-dev-middleware 並實作了 server.js:
    node server.js

  • 如果是用 webpack-dev-server:
    webpack-dev-server

  • 如果都沒用,只用 webpack:
    webpack --watch
    PS. 至少要找一個 server 並啟動它喔 ~

然後只要讓瀏覽器連到 index.html 就可以看到 JSX 自動編譯,即時變更網頁內容了!


總結

環境配置懶人包

  • npm install --save-dev webpack babel-loader css-loader style-loader sass-loader less-loader url-loader express webpack-dev-middleware webpack-hot-middleware
    • npm install --save-dev babel-loader css-loader style-loader sass-loader less-loader url-loader loaders
    • npm install --save express Express
    • npm install --save-dev webpack-dev-middleware webpack-dev-middleware
    • npm install --save-dev webpack-hot-middleware HMR - webpack-dev-middleware
    • npm install --save-dev react-hot-loader HMR - webpack-dev-server

Webpack 篇就這樣告一段落囉,下篇 講的是 React Router 以及 Redux 框架。如果有興趣可以繼續收看~

這篇會不定期更新,如果有打錯或有任何疑問都歡迎留言告知喔~
我們下篇再見~