
簡介
Webpack 是一個 module bundler,把許多不同類型的模組 build 成靜態資源。
主要功能:
- 將 CSS、圖片與其他資源打包
 
- Pre-processing Less、CoffeeScript、JSX、ES6 等檔案
 
- 可依 entry 文件不同,把 .js 打包成多個 .js 檔案
 
- 可以搭配許多 Loaders
 
這一篇文章主要是紀錄 Webpack 搭配 Express、Hot module replacement 、 Babel 和許多常用的 Lodaers 的設定及使用方式.
Loaders 介紹
在使用 Webpack 時,常常會搭配各種 loaders 來做靜態資源的打包,接下來我們先介紹一下常用到的 lodaers:
babel-loader
Babel 是一個JavaScript compiler,可以將新的 JS 語法轉譯為瀏覽器支援的 ES5,因為 JavaScript 幾乎是每年會提出一個新的規格草案,但是瀏覽器沒辦法很快就能夠支援新的 JS,所以我們需要先轉譯成瀏覽器支援的 ES5。
設定方式:
.babelrc:
1 2 3
   | {     "presets": ["es2017", "react"] }
  | 
 
webpack.config.js:
1 2 3 4 5 6 7 8 9 10 11
   | {     module: {         rules: [{             test: /\.(js|jsx)$/,             exclude: '/node_modules/',             use: {                 loader: 'babel-loader'             }         }]     } }
  | 
 
css-loader and style-loader
css-loader: 載入 CSS file.
style-loader: Add CSS to the DOM by injecting a <style> tag. 通常搭配css-loader一起使用。
webpack.config.js:
1 2 3 4 5 6 7 8
   | {     module: {         rules: [{             test: /\.css$/,             use: ['style-loader', 'css-loader']         }]     } }
  | 
 
使用方式:
1 2
   |  import './css/style.css';
 
  | 
 
less-loader
將 Less 轉為 CSS.
webpack.config.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | {     module: {         rules: [{             test: /\.less$/,             use: [{                 loader: "style-loader"              }, {                 loader: "css-loader"              }, {                 loader: "less-loader"              }]         }]     } }
  | 
 
使用方式:
1 2
   |  import './css/style.less';
 
  | 
 
file-loader
載入檔案,回傳 public URL.
webpack.config.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | {     module: {         rules: [{             test: /\.(jpe?g|JPE?G|png|PNG|gif|GIF|svg|SVG|woff|woff2|eot|ttf)(\?v=\d+\.\d+\.\d+)?$/,             use: [{                 loader: 'file-loader',                 options: {                     name: '[hash].[ext]',                         publicPath: "/src/",                     outputPath: "img/"                 }             }]         }]     } }
  | 
 
使用方式:
url-loader
和 file-loader 類似, 但在檔案大小小於 limit 時, 會回傳一個 base64 url.
webpack.config.js:
1 2 3 4 5 6 7 8 9 10 11 12 13
   | {     module: {         rules: [{             test: /\.(jpe?g|JPE?G|png|PNG|gif|GIF|svg|SVG|woff|woff2|eot|ttf)(\?v=\d+\.\d+\.\d+)?$/,             use: [{                 loader: 'url-loader',                 options: {                     limit: 8192                      }             }]         }]     } }
  | 
 
使用方式:
1 2 3 4 5 6
   |  import user_icon from './img/user-icon.png';
  let img = document.createElement("img"); img.src = user_icon; $("#userIcon").append(img);
 
  | 
 
Plugins
Webpack 也提供了許多方便的 plugins, 這裡介紹一下常用的 plugins:
uglifyjs-webpack-plugin
uglifyjs-webpack-plugin 主要功能是 minify JavaScript, 減少 js file size.  
webpack-dev-middleware
webpack-dev-middleware 可以讓 webpack 被 Express app 或是其他可使用 middleware 的框架所使用。
webpack-dev-server
webpack-dev-server 本身就是小型的 Express server,整合了 Express 和 webpack-dev-middleware。
雖然方便架設,但彈性較少,無法跟現有的 Express app 結合。
Hot Module Replacement
Hot Module Replacement (簡稱 HMR),主要功能是在修改任意檔案並儲存後,會自動編譯打包 (等同於 webpack --watch 的功能),接著更新瀏覽器畫面,只會 reload 更新的部分,而不會全部 reload。
HMR 有兩種套件:
- react-hot-loader: webpack-dev-server 的衍生套件。
 
- webpack-hot-middleware: webpack-dev-middleware 的衍生套件。
 
請依照選擇的環境安裝。
這裡我們選擇的環境是 webpack-dev-middleware + Express + webpack-hot-middleware, Dashboard 則是使用webpack-dashboard.
安裝
安裝 Webpack and Loaders
1 2 3 4 5 6 7 8
   |  $ npm install --save-dev webpack webpack-cli
 
  $ npm install --save-dev css-loader style-loader url-loader file-loader less less-loader
 
  npm install --save-dev babel-core babel-loader babel-preset-es2017
 
  | 
 
安裝 webpack-dev-middleware、webpack-hot-middleware
1 2 3 4 5
   |  $ npm install --save-dev webpack-dev-middleware webpack-hot-middleware
 
  $ npm install --save-dev uglifyjs-webpack-plugin
 
  | 
 
安裝 webpack-dashboard: A friendly UI for webpack.
1
   | $ npm install --save-dev webpack-dashboard
   | 
 
Webpack 相關指令
1 2 3 4 5
   | webpack                        webpack -p                     webpack --watch                webpack -d                     webpack --progress --color    
   | 
 
設定 Webpack
這裡的設定範例是依照我們所選擇的環境: webpack-dev-middleware + Express + webpack-hot-middleware, 並使用以下 loaders:
- babel-loaders
 
- css-loader
 
- style-loader
 
- less-loader
 
- url-loader
 
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 52
   | const path = require('path'); const webpack = require('webpack'); const DashboardPlugin = require('webpack-dashboard/plugin');
  const env = process.env.NODE_ENV; module.exports = {     mode: env || 'development',     entry: [         'webpack-hot-middleware/client?reload=true',             './public/js/index.js'                               ],     output: {         path: path.join(__dirname, '/dist/'),         filename: 'bundle.js',         publicPath: '/src/'     },     module: {         rules: [{             test: /\.(js|jsx)$/,             exclude: '/node_modules/',             use: {                 loader: 'babel-loader'             }         }, {             test: /\.css$/,             use: ['style-loader', 'css-loader']         }, {             test: /\.less$/,             use: [{                 loader: "style-loader"               }, {                 loader: "css-loader"                 }, {                 loader: "less-loader"                }]         }, {             test: /\.(jpe?g|JPE?G|png|PNG|gif|GIF|svg|SVG|woff|woff2|eot|ttf)(\?v=\d+\.\d+\.\d+)?$/,             use: [{                 loader: 'url-loader',                 options: {                     limit: 8192                          }             }]
          }]     },     devtool: 'cheap-module-eval-source-map',         plugins: [         new webpack.HotModuleReplacementPlugin(),              ] };
  | 
 
搭配 Babel, 需安裝 babel-loader 並另外再設定 .babelrc:
1 2 3
   | {     "presets": ["es2017"] }
  | 
 
可以在 package.json 加上以下 script, 方便之後執行:
1 2 3 4 5 6 7
   | {     "scripts": {         "test": "echo \"Error: no test specified\" && exit 1",         "dev": "NODE_ENV=development webpack-dashboard -p 2458 -- node app.js",         "deploy": "NODE_ENV=production webpack -p --config webpack.config.js"     } }
  | 
 
其中,dev指令中的-p可換為指定的port.
撰寫 app.js
再來,我們先撰寫一個簡單的 app.js, 並在裡面加上 Webpack、webpack-dev-middleware、HMR 和 webpack-dashboard 的相關設定:
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
   | const express = require('express'); const session = require('express-session'); const bodyParser = require('body-parser'); const cookieParser = require('cookie-parser'); const path = require('path'); const logger = require('morgan'); const helmet = require('helmet'); const http = require('http'); const https = require('https'); const webpack = require('webpack'); const webpackDev = require('webpack-dev-middleware'); const webpackHot = require('webpack-hot-middleware'); const DashboardPlugin = require('webpack-dashboard/plugin'); const webpackConfig = require('./webpack.config'); const config = require('./config'); const routes = require('./routes');
 
  const app = express(); app.use(express.static(path.join(__dirname, 'public'))); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs');
  app.set('port', process.env.PORT || 3000); app.set('httpsport', process.env.HTTPSPORT || 3001);
  app.use(helmet()); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: false})); app.use(cookieParser()); app.use('/', routes);
 
  app.set('trust proxy', 1)  app.use(session({     secret: config.server.secret,     cookie: {         maxAge: 86400 * 1000     },     resave: true,     saveUninitialized: true })); app.use((req, res, next) => {     res.locals.login = req.session.login;     res.locals.account = req.session.account;     next(); });
 
  const compiler = webpack(webpackConfig); compiler.apply(new DashboardPlugin());
 
  app.use(webpackDev(compiler, {     noInfo: true,     publicPath: webpackConfig.output.publicPath }));
 
  app.use(webpackHot(compiler));
 
 
  app.use((req, res, next) => {     let err = new Error('Not Found');     err.status = 404;     next(err); });
  app.use((err, req, res, next) => {          res.locals.message = err.message;     res.locals.error = req.app.get('env') === 'development' ? err : {};
           res.status(err.status || 500);     res.render('error'); });
 
 
  let httpServer = app.listen(app.get('port'), (err) => {     if(err) {         console.error(err);     } else {         console.log(`Listening at port ${httpServer.address().port}.`);     } });
 
 
 
 
 
 
 
 
 
 
 
 
 
  | 
 
啟動 Server
依照上述步驟設定好 Webpack,並撰寫好 app.js 後,要記得在 HTML 中加入打包好的 JS:
1
   | <script src="/src/bundle.js"></script>
   | 
 
最後就可以啟動 Server:
1 2 3 4 5
   |  $ npm run dev
 
  $ npm run deploy
 
  | 
 
這樣就完成了使用 Webpack 來打包我們的網站囉~
參考資料