本文同步自個人博客http://joeray61.comcss
本文是一個簡單的webpack入門教程,但願可以幫助webpack初學者快速上手。若有錯誤,敬請斧正。
本文全部的demo均可以在webpack-demo裏找到,git clone
以後須要執行npm install
安裝全部依賴包。html
webpack
是一個模塊打包工具,出自德國開發者Tobias Koppers
之手,處理速度很快。在webpack
看來,一切資源均可以是一個模塊,不只限於js文件。正是因爲這些特色,使得webpack
能夠替代Grunt
和Gulp
,成爲了目前業內最火的前端構建工具。前端
webpack能夠安裝到全局,也能夠做爲項目的依賴工具安裝到項目中node
// 全局安裝 $ npm install -g webpack $ npm install -g webpack-dev-server
若是報沒有權限的話,在命令前面加上sudo
就能夠了react
// 局部安裝 $ npm install --save-dev webpack $ npm install --save-dev webpack-dev-server
能夠看到,咱們在安裝webpack
的同時,也安裝了webpack-dev-server
。那麼webpack-dev-server
是什麼呢?它是一個輕量級的基於express
的node.js
服務器,用來服務資源文件。不是必須的,可是建議一塊兒安裝。webpack
$ webpack main.js bundle.js
以上是直接使用webpack
命令行來進行打包的命令,把main.js
構建成bundle.js
,具體示例能夠查看demo1,在文件夾中執行上方的命令便可。git
webpack
有一些參數是咱們應該要知道的github
webpack
: 構建一次開發版本web
webpack -p
: 構建一次產品版本,與開發版本的區別是會對文件進行壓縮正則表達式
webpack -d
: 添加source map
webpack --watch
: 監聽文件的改動,持續增量構建
webpack --colors
: 讓命令行的輸出更好看一點(實際使用zsh
的命令行時發現並無區別)
webpack
還有不少強大的功能,這些均可以在命令行使用,可是若是都寫在命令行,既不方便使用,也不能直觀地反映各個配置項,這時候,就須要配置文件登場了。
webpack
默認使用的配置文件名是webpack.config.js
,也能夠經過--config
參數在命令行指定另外一個命名的配置文件。配置文件其實也是一個模塊,全部的構建信息都放在module.exports
中。下面咱們就來說一講各個重要的配置項。
如下各個示例請在各demo文件夾中執行
webpack-dev-server
進行查看
入口文件的配置項名稱是entry
,表明了webpack
給整個項目進行打包的一個主入口,能夠搭配output
屬性指定的輸出文件來使用
// webpack.config.js module.exports = { entry: 'main.js', output: { filename: 'bundle.js' } }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo2</title> </head> <body> <script src="bundle.js"></script> </body> </html> // main.js document.write('<h1>Hello Webpack!</h1>');
示例見demo2
在多頁的應用中,咱們須要有多個入口文件,這在webpack
中也是能夠支持的。
// webpack.config.js module.exports = { entry: { profile: './profile.js', feed: './feed.js' }, output: { filename: '[name].bundle.js' } }; // feed.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo3-Feed</title> </head> <body> <script src="feed.bundle.js"></script> </body> </html> // feed.js document.write('<h1>Hello Feed!</h1>'); // profile.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo3-Profile</title> </head> <body> <script src="profile.bundle.js"></script> </body> </html> // profile.js document.write('<h1>Hello Profile!</h1>');
示例見demo3,在demo3文件夾中啓動webpack-dev-server
,訪問localhost:8080/profile.html
和localhost:8080/feed.html
查看效果。
Loaders
顧名思義就是加載器,用於加載各類格式的文件,例如React
使用的JSX
,ES6
和ES7
,圖片、CSS
、JSON
文件等,可謂是webpack
中最核心的功能之一。官方文檔中列出了全部可用的loaders。
loaders
配置在module.exports
中的module
字段下(提及來有點繞,待會兒看代碼就懂了),是一個數組,表明loader
的集合,每個loader
有幾個配置項:
test
: 正則表達式,用於匹配文件的路徑,通常都直接用來匹配文件後綴,在複雜的場景下,能夠針對同一類型的不一樣文件作不一樣處理
loader
: loader
的名稱,實際使用時能夠省略-loader
的後綴(例如css-loader
,能夠寫成css
)
include
: 必須包含的文件路徑
exclude
: 不須要處理的文件路徑
query
: loader
的額外設置選項
下面我介紹幾個經常使用的loader。
先安裝style-loader
和css-loader
。其中,style-loader
用來向 HTML 頁面中插入<style>
,css-loader
用來讀取 css 文件。
$ npm install --save-dev style-loader css-loader
而後在webpack.config.js
中配置loader
module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.css$/, loader: 'style!css' } ] } }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo4</title> </head> <body> <h1>Hello World!</h1> <script src="bundle.js"></script> </body> </html> // a.css h1 {color: red;} // main.js require('./a.css');
示例見demo4,能夠看到,多個loader
之間用!
鏈接。在本地用webpack-dev-server
啓動服務,訪問localhost:8080
能夠看到h1
中的字體顏色如a.css
定義的那樣顯示紅色
這個 demo 中的 css 是對全局生效的,若是須要只對當前模塊生效,就要用到咱們下面介紹的css module
。
近幾年,模塊化這個概念在前端被不斷地被說起。模塊化使得代碼被分割成一個個更小的、獨立的、可維護性更高的獨立單元。相比於JS
,CSS
的模塊化發展的相對慢一點。最近出現了一個叫作css modules
的技術,在css模塊中,全部的類名和動畫名默認只對向前模塊生效。webpack
對css module
提供了不錯的支持,只須要在css-loader
後面加上?module
便可使用。若是要對全局生效,可使用:global()
,把選擇器做爲參數傳入。
// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.css$/, loader: 'style!css?module' } ] } }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo5</title> </head> <body> <div class="foo">Foo</div> <div class="bar">Bar</div> <script src="bundle.js"></script> </body> </html> // a.css .foo {color: red;} :global(.bar) { color: green; } // main.js var style = require('./a.css'); document.write('<div class=' + style.foo + '>Module - Foo</div>'); document.write('<div class="bar">Module - Bar</div>');
須要注意的是,在main.js
中使用時,必須用變量style.foo
,直接寫成class="foo"
是不會生效的,由於a.css
中的.foo
的類名被編譯了,因此導出的style.foo
將不等於foo
。
示例代碼見demo5。在 demo5 的目錄下執行webpack-dev-server
,在瀏覽器中打開localhost:8080
,能夠看到對應的效果。
先安裝url-loader
和file-loader
$ npm install --save-dev url-loader file-loader
注意,
url-loader
對file-loader
有依賴,可是安裝url-loader
時不會自動安裝file-loader
,這裏我暫時沒有花時間去了解緣由,先都手動安裝吧。若是不安裝file-loader
,超過limit
大小的文件將沒法加載。
url-loader
是一個文件加載器,咱們能夠給他設定一個limit
,用來限定文件大小,文件小於這個大小時會被轉換成一個Data Url
,反之轉換成普通url表示資源路徑。咱們經常使用url-loader
來加載圖片。
// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' } ] } }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo6</title> </head> <body> <script src="bundle.js"></script> </body> </html> // main.js var img1 = new Image(); img1.src = require('./webpack.png'); document.body.appendChild(img1); var img2 = new Image(); img2.src = require('./wade.jpg'); document.body.appendChild(img2);
示例代碼見demo6。在demo6目錄下運行webpack-dev-server
,在瀏覽器中訪問localhost:8080
,審查元素能夠看到,8k如下的圖片使用了Data Url
,8k以上的圖片使用的是資源地址。
現現在,ES6
、React
可謂是紅透半邊天,提及ES6
、React
,就不得不提Babel
了。Babel
是一個 Javascript 編譯工具,可讓你如今就能使用目前還未被瀏覽器徹底支持的下一代JS(ES6/ES7),或是基於 JS 進行擴展的 JSX 等。
咱們先安裝須要的包
$ npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom
其中,babel-core
是babel
的核心功能,babel-loader
是webpack
使用的babel
加載器,babel-preset-es2015
和babel-preset-react
是babel
用來解析ES6
和JSX
的包,react
和react-dom
是開發react
的依賴包。
// webpack.config.js module.exports = { entry: './main.jsx', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.js[x]?$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['es2015', 'react'] } } ] } }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo7</title> </head> <body> <div id="root"></div> <script src="bundle.js"></script> </body> </html> // main.jsx import React from 'react'; import {render} from 'react-dom'; render( <h1>Hello world!</h1>, document.querySelector('#root') );
能夠看到,在這個配置中,咱們把babel-loader
的配置寫在query
字段中。其實也能夠直接寫在loader
字段中,loader: 'babel-loader?presets[]=es2015&presets[]=react'
,可是這樣就不是太清晰了,建議配置比較長時拿出來放到query
字段中去。
示例代碼見demo7,在demo7目錄下執行webpack-dev-server
,在瀏覽器中訪問localhost:8080
查看效果。
plugins
是webpack
中另外一個很是重要且又用的配置,它與loaders
的區別在於,loader
是用來加載文件的,會逐個文件處理,而plugins
是做爲webpack
功能的補充,整個構建過程都在持續發揮做用。下面我來給你們介紹幾個經常使用的插件。
這個插件估計不少人看名字就猜出來了,是用於壓縮 JS 代碼的。它是webpack
的內置插件,不須要安裝就可使用。
直接上代碼吧:
// webpack.config.js var webpack = require('webpack'); var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, plugins: [ new UglifyJsPlugin() ] }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo8</title> </head> <body> <script src="bundle.js"></script> </body> </html> // main.js var name = 'JoeRay61'; var age = 24; document.write('My name is ' + name + ', I\'m ' + age + ' years old.');
示例代碼見demo8。在demo8目錄下執行webpack-dev-server
,在瀏覽器中訪問localhost:8080
,打開開發這工具,能夠看到bundle.js的代碼被壓縮成了一行,變量名也被替換了,說明插件生效了。
!function(r){function e(t){if(o[t])return o[t].exports;var n=o[t]={exports:{},id:t,loaded:!1};return r[t].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var o={};return e.m=r,e.c=o,e.p="",e(0)}([function(r,e){var o="JoeRay61",t=24;document.write("My name is "+o+", I'm "+t+" years old.")}]);
想象這樣一種場景,咱們但願在開發環境中在代碼中輸出一些調試信息,那麼如何在webpack
中實現呢。這時候就須要用上Feature Flags
。它是藉由webpack
的自定義插件機制實現的全局環境變量,咱們能夠在代碼中判這些全局變量的值來實現定製化的功能。
// webpack.config.js var webpack = require('webpack'); var flags = new webpack.DefinePlugin({ __PROD__: JSON.stringify(JSON.parse(process.env.PROD || 'false')) }); module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, plugins: [flags] }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo9</title> </head> <body> <script src="bundle.js"></script> </body> </html> // main.js document.write('<h1>Hello world!</h1>'); if (!__PROD__) { document.write('<p>it is dev version</p>'); }
示例代碼見demo9。進入demo9目錄,分別執行PROD=1 webpack-dev-server
和webpack-dev-server
,在瀏覽器中訪問localhost:8080
,能夠看到區別。
HMR
是webpack
中一個激動人心的功能,中文直譯叫模塊熱替換
,是指配合webpack-dev-server
的服務器,在你修改了項目的模塊以後,不須要手動刷新頁面,便可以看到更新後的效果,有效地提高了開發效率。
配合webpack-dev-server
,咱們有2種方式能夠啓用該功能:
在命令中中webpack-dev-server
命令後面追加--hot
和--inline
參數
--hot
: 添加HMR插件,將服務器切換到 hot 模式
--inline
: 將webpack-dev-server
的 runtime 加入到打包後的文件中
--hot --inline
: 當這兩個參數並存時,會額外添加一個webpack/hot/dev-server
的entry
配置webpack.config.js
添加new webpack.HotModuleReplacementPlugin()
插件
添加webpack/hot/dev-server
和webpack-dev-server/client?http://localhost:8080
這2個entry
針對第2中方式咱們來測試一下:
// webpack.config.js var webpack = require('webpack'); module.exports = { entry: [ 'webpack/hot/dev-server', 'webpack-dev-server/client?http://localhost:8080', './main.js' ], output: { filename: 'bundle.js' }, plugins: [ new webpack.HotModuleReplacementPlugin() ] }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo10</title> </head> <body> <script src="bundle.js"></script> </body> </html> // main.js document.write('<h1>Hello world!</h1>');
注意,測試過程當中我發現,若是原來是全局安裝的
webpack-dev-server
,必需要在項目中局部安裝webpack-dev-server
,不然啓動服務器時會報錯
安裝命令$ npm install --save-dev webpack-dev-server
示例代碼見demo10。在demo10目錄下,執行webpack-dev-server
,在瀏覽器中查看localhost:8080
,發現輸出Hello world!
,這時候不要關閉服務器,直接修改main.js
的代碼,保存後查看瀏覽器效果,發現雖然沒有手動刷新頁面,可是效果已經出來了。
當幾個不一樣的腳本有公共的部分時,咱們能夠把公共部分抽出來放到一個單獨的文件中。
// webpack.config.js var webpack = require('webpack'); module.exports = { entry: { index: './index.js', main: './main.js' }, output: { filename: '[name].bundle.js' }, plugins: [ new webpack.optimize.CommonsChunkPlugin('common.js') ] }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo11</title> </head> <body> <script src="common.js"></script> <script src="index.bundle.js"></script> <script src="main.bundle.js"></script> </body> </html> // index.js var data = require('./data'); document.write('<p>foo is ' + data.foo + '</p>'); // main.js var data = require('./data'); document.write('<p>bar is ' + data.bar + '</p>'); // data.js var data = { foo: 123, bar: 456 }; module.exports = data;
具體示例見demo11。在demo11目錄下執行webpack-dev-server
,在瀏覽器中訪問localhost:8080
,打開開發者工具,發現data.js
被抽出單獨放到了common.js
中。
本文是我本身學習webpack
的一個筆記,我把它記錄下來,但願能給其餘的webpack
初學者提供一點幫助,謝謝觀看,歡迎交流!