WebPack入門教學
Webpack與其他前端打包工具(watchify、Browserify)定位不太同,它是一個模組(module)系統,透過這些豐富的模組來實現更多的功能,它有:
- 將CSS、圖片與其他資源打包至一個.js之中。
- 打包之前預處理(less、coffeescript、jsx等)。
- 依entry文件不同,把.js折分為多個.js
- 豐富的模組元件
安裝webpack
參考 https://webpack.github.io/docs/tutorials/getting-started/與
首先你必須安裝Node.js,我們需要使用npm進行套件的安裝與管理。
專案:npm install webpack --save-dev (儲存於專案目錄下)
全域:npm install webpack -g (global,存儲於本機之下)
webpack指令
開啟cmd.exe輸入指令:webpack main.js bundle.js。
通常會建立一個
webpack.config.js
檔案並且進組態。
以是常用的指令:
- webpack:會在開發模式下開始一次性的建置
- webpack -p:會建置 production-ready 的程式碼 (壓縮)
- webpack --watch:會在開發模式下因應程式碼修改(儲存時有異動)持續更新建置
- webpack -d:加入 source maps 檔案
- webpack --progress --colors:含處理進度與顏色
常用指令:webpack --progress --colors --watch
Loader
webpack僅能處理Javascript所以需要一些Loader來幫忙。例如,處理CSS檔案,我們需要使用style-loader
。
安裝loader:npm install css-loader style-loader
JSXloader:npm install jsx-loader
webpack.config.js
module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.css$/, loader: "style!css" }, { test: /\.js$/, loader: 'jsx-loader?harmony' } // loaders 可以像 querystring 一樣接收參數 ] }, resolve: { // 設定後只需要寫 require('file') 而不用寫成 require('file.jsx') extensions: ['', '.js', 'jsx', '.json'] } };
- loaders 指定要載入的loader,loaders 可以像 querystring 一樣接收參數。
- resolve 如果希望在
require()
時不需要加入副檔名可以像下面範例加入一個 resolve.extensions 屬性並告訴 webpack 哪些副檔名是可以省略的。
開發伺服器
安裝:npm install webpack-dev-server -g
啟動:webpack-dev-server --progress --colors
webpack-dev-server會讀取webpack.config.js
,依照畫面輸出訊息可以得到伺服器位置,現在你可以一邊修改程式碼一邊看到即時更新網頁內容。
package.json
參考:https://github.com/ruanyf/webpack-demos。
切換至專案目錄下,於 cmd.exe 執行 npm init 並回答問題會產生可以幫忙產生預設package.json。package.json主要用於管理套件。在scripts段落可以設計多個指令,並使用npm run [指令名稱]來執行指令。
// package.json { // ... "scripts": { "dev": "webpack-dev-server --devtool eval --progress --colors", "deploy": "NODE_ENV=production webpack -p" }, // ... }
cmd.exe輸入npm run dev。我們可以在scripts段落設計各種所需要的指令。
Demo:多檔案輸出
很多情況,我們會希望不合併為一個.js檔案,希望分別輸出,我們可以這樣設計:
module.exports = { entry: { bundle1: './main1.js', bundle2: './main2.js' }, output: { filename: '[name].js' } };
bundle1、bundle2
會成為 [name]
的參數,main1.js 會輸出為 bundle1.js,main2.js 會輸出為 bundle2.js。
Demo:Babel-loader
Babel-loader 可以幫助你將JSX/ES6檔案轉換為JS檔案。
安裝:npm install react --save-dev (我們會使用 React 的 JSX 語法,所以需要它)
安裝:npm install babel-loader --save-dev
main.jsx是一個JSX檔案
var React = require('react'); React.render( <h1>Hello, world!</h1>, document.body );
webpack.config.js
module.exports = { entry: './main.jsx', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.js[x]?$/, exclude: /node_modules/, loader: 'babel-loader' }, ] } };
loaders指定了使用 Babel-loader 來將 main.jsx 轉換至 bundle.js。
Demo:css-loader
webpack 允許你在JS檔案中參考CSS。例如:
main3.js
require('./app.css'); document.write('<h1>Hello world!</h1>');
透過 require()
載入 app.css。如果 webpack.config.js 有設置 resolve
段落,可省略副檔名。
app.css
body { background-color: blue; }
webpack.config.js
module.exports = { entry: './main3.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.css$/, loader: 'style-loader!css-loader' }, ] } };
Demo:Image loader
安裝:npm install url-loader --save-dev
某些場合,需要載入圖片,那麼可以使用 url-loader
main4.js
var img1 = document.createElement("img"); img1.src = require("./small.png"); document.body.appendChild(img1); var img2 = document.createElement("img"); img2.src = require("./big.png"); document.body.appendChild(img2);
webpack.config.js
module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders:[ { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' } ] } };
url-loader 會轉換圖片,依照圖片的大小(預設 8192 bytes)來決定轉換後的類別。小於 8k 會轉換為 Data URL。
<img src="data:image/png;base64,iVBOR...uQmCC"> <img src="4853ca667a2b8b8844eb2693ac1b2578.png">
Loader
參考:http://webpack.github.io/docs/using-loaders.html
安裝loader
npm install xxx-loader --save
npm install xxx-loader --save-dev
在使用時,xxx-loader可簡寫為xxx,例如,json-loader
簡寫為json
。
使用有三種方式:
- 明確的require敘述
- 經由組態檔
- 經由CLI
使用require
盡量避免。
組態檔
透過 RegExp 在組態檔中去綁定副檔名與處理的loader關係。(建議使用)
{ module: { loaders: [ { test: /\.jade$/, loader: "jade" }, // ".jade" 副檔名使用 "jade" loader { test: /\.css$/, loader: "style!css" }, // ".css" 副檔名使用 "style" 和 "css" loader // ! 是 chain 的概念 // 也可使用陣列語法 { test: /\.css$/, loaders: ["style", "css"] }, ] } }
CLI
webpack --module-bind jade --module-bind 'css=style!css'
loader 可以指定參數,採用一般QueryString方法:?key=value&key2=value2
,也接受JSON物件:?{"key":"value","key2":"value2"}
。
CSS Module
css-loader?modules
可以啟用 CSS Modules 功能。
UglifyJS
Webpack 的 plugin 系統可以擴充它的功能。例如,使用http://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin來進行JS壓縮。
web.config.js
var webpack = require('webpack'); var uglifyJsPlugin = webpack.optimize.UglifyJsPlugin; module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, plugins: [ new uglifyJsPlugin({ compress: { warnings: false } }) ] };
輸出的 bundle.js 就會進行壓縮處理。
Code Splitting
一般,我們在大型應用上把所有程式碼壓縮為單一檔案並不是有效的。這樣會造成檔案過於肥大。webpack也能讓我們可以來分割它們。需要的地方分別載入。
首先,使用require.ensure
來定義分割點(split point)。
main8.js
require.ensure(['./a'], function(require) { // 內容由 a 模組取得 var content = require('./a'); document.open(); document.write('<h1>' + content + '</h1>'); document.close(); });
require.ensure是向webpack說明會使用到那些模組,這些模組就是我們的分割點。
如果是多個模組
require(["module-a", "module-b"], function(a, b) { // ... });
a.js (a模組)
module.exports = 'Hello World';
web.config.js
module.exports = { entry: './main.js', output: { filename: 'bundle.js' } };
index.html
<html> <body> <script src="bundle.js"></script> <body> </html>
編譯後會除了預設的bundle.js
,還會產生1.bundle.js
,main.js
→ bundle.js
;a.js
→ 1.bundle.js
,最後,bundle.js
內去會參考1.bundle.js
。
Code Splitting with bundle-loader
安裝:npm install bundle-loader --dev-save
另一種Code Splitting的方式是使用bundle-loader
.
mail9.js
var load = require('bundle-loader!./a.js'); load(function(fileContent) { document.open(); document.write('<h1>' + fileContent + '</h1>'); document.close(); });
首先透過bundle-loader
將a.js
模組載入,在load()
去使用載入的內容。
共同區塊
當多個腳本檔案有共同區塊時,你可以將共同部分提取出來為一支共用檔。
main10-x.js
// main10-1.jsx var React = require('react'); React.render( <h1>Hello World</h1>, document.getElementById('a') ); // main10-2.jsx var React = require('react'); React.render( <h2>Hello Webpack</h2>, document.getElementById('b') );
init.js
是預設提取出來的JS檔。
<html> <body> <div id="a"></div> <div id="b"></div> <script src="init.js"></script> <script src="bundle1.js"></script> <script src="bundle2.js"></script> </body> </html>
web.config.js
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin"); module.exports = { entry: { bundle1: './main10-1.jsx', bundle2: './main10-2.jsx' }, output: { filename: '[name].js' }, module: { loaders:[ { test: /\.js[x]?$/, exclude: /node_modules/, loader: 'babel-loader' }, ] }, plugins: [ new CommonsChunkPlugin('init.js') ] }
指定使用CommonsChunkPlugin,並指定產出的名稱為init.js。
Vendor 區塊
在CommonsChunkPlugin裡,你可以加入額外的vendor函式庫到分割檔中。
安裝:npm install jquery --dev-save
這裡我們安裝jquery模組,並在main11.js載入使用。
main11.js
var $ = require('jquery'); $('h1').text('Hello World');
index.html
<html> <body> <h1></h1> <script src="vendor.js"></script> <script src="bundle.js"></script> </body> </html>
web.config.js
var webpack = require('webpack'); module.exports = { entry: { app: './main.js', vendor: ['jquery'], }, output: { filename: 'bundle.js' }, plugins: [ new webpack.optimize.CommonsChunkPlugin(/* chunkName= */'vendor', /* filename= */'vendor.js') ] };
使用上一個範例的宣告方式也行:
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin"); // ... plugins: [ new CommonsChunkPlugin('vendor', 'vendor.js') ]
如果你想將(jquery)模組使用在每一個模組中,像是讓$
或jQuery
都是使用在每一模組中,但不需要寫required('jquery')
,你應該使用ProvidePlugin
。文件:http://webpack.github.io/docs/shimming-modules.html。
main11.js
//var $ = require('jquery'); $('h1').text('Hello World');
註解require()
的使用。
web.config.js
module.exports = { entry: { app: './main.js' }, output: { filename: 'bundle.js' }, plugins: [ new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery", "window.jQuery": "jquery" }) ] };
Webpack就是幫我們把jQuery模組包裝在bundle.js之中。
index.html
<html> <body> <h1></h1> <script src="bundle.js"></script> </body> </html>
這樣就能刪除(或取消)vendor.js的使用。
全域變數
如果你想使用某些全域變數,但你不想包含在Webpack的bundle.js之中,你在web.config.js中去啟用externals
。文件:http://webpack.github.io/docs/library-and-externals.html。
Notifier
安裝:npm install webpack-notifier --save-dev
var path = require('path'); var WebpackNotifierPlugin = require('webpack-notifier');
module.exports = { context: path.join( dirname, 'App'), entry: './main.js', output: { path: path.join(dirname, 'Built'), filename: '[name].bundle.js' }, plugins: [ new WebpackNotifierPlugin() ], module: { loaders: [ { test: /.css$/, loader: "style!css" }, { test: /.jpe?g$|.gif$|.png$|.svg$|.woff$|.ttf$|.eot$/, loader: "url" } ] } };
我們觸發webpack時可以看到桌面會跳出一個提醒訊息。
謝謝分享這麼完整的整理,讓我可以很快速的對webpack有完整的了解。
回覆刪除不客氣。
刪除謝謝你的教學
回覆刪除Bruce大, 請問如果沒有自動產生webpack.config.js , 我應該怎麼做, 謝謝您 (剛發錯篇了, 抱歉)
回覆刪除它只是一個純文字的組態檔,你可以複製此頁面上的設置去修改即可。
刪除謝謝大大回覆,不好意思再請教一個可能不是很相關的問題。 我是用Asp.net MVC 5 搭配 reactjs.net 來開發~ 使用webpack 打包與babel轉譯, 我在使用範例的時候, require('xxx') 一直被認為是undefined , 是因為這是node.js的語法嗎? 如果我在asp.net mvc 把component切成分開的檔案想互相引用的話, 請問該怎麼處理, 謝謝您。
刪除