寫於 2016.08.03css
原文鏈接:Webpack — The Confusing Partshtml
Webpack是React和Redux項目的主要模塊加載器。我認爲使用Angular2和其餘的框架的人在現在也大量使用Webpack進行開發。node
當我第一次查看Webpack的配置文件時,我是懵逼的。在使用過一段時間之後,我以爲這是由於Webpack有着獨一無二的語法和標新立異的哲學思想,因此在剛開始使用的時候可能會形成必定的困惑。湊巧的是,這些哲學思想也是讓它如此受歡迎的緣由。react
正由於Webpack的起步比較容易產生困惑,因此我但願寫一些什麼出來,好讓更多人更容易上手而且體驗它強大的特性。webpack
接下來是第一部分。web
兩個核心哲學思想是:npm
一切都是模塊——就像JS文件能夠視做「模塊」同樣,其餘全部的一切(CSS,圖片,HTML)均可以被視做模塊。也就是說,你能夠require(「myJSfile.js」)
或者require(「myCSSfile.css」)
。這意味着咱們能夠把任何靜態資源分割成可控的模塊,以供重複使用等不一樣的操做。json
只加載「你須要的」和「你什麼時候須要」的——典型的模塊加載器會把全部的模塊最終打包生成一個巨大的「bundle.js」文件。但在不少實際的項目當中,這個「bundle.js」文件體積可能會達到10MB~15MB,而且會一直不停進行加載!因此Webpack經過大量的特性去分割你的代碼,生成多個「bundle」片斷,而且異步地加載項目的不一樣部分,所以只會爲你加載「你須要的」和「你什麼時候須要」的部分。數組
OK,讓咱們一塊兒來看看那些「讓人困惑」的部分吧。瀏覽器
第一件須要意識到的事情是Webpack擁有着大量的特性。有一些是「開發環境專用」的,一些是「生產環境專用」的,還有一些是「通用」的。
通常來講,大部分的項目都使用了許多Webpack的特性,因此它們一般有兩個大的
webpack config
文件,用於區分開發環境和生產環境。
明白Webpack這個模塊加載器擁有兩個接口是很是重要的:
webpack.config.js
)的配置項來控制Webpack的打包動做。你剛開始學習Webpack的時候多是從CLI入手的,但你接下來極可能只會用它去創建生產環境的項目。
使用方法:
OPTION 1:
//全局安裝
npm install webpack --g
//在終端使用
$ webpack //<--經過webpack.bundle.js進行打包
OPTION 2 :
//本地安裝並寫入package.json依賴
npm install webpack --save
//添加到package.json的script內
「scripts」: {
「build」: 「webpack --config webpack.config.prod.js -p」,
...
}
//開始構建
npm run build
複製代碼
這是一個運行在8080端口的基於Express的node.js服務器。這個服務器會在內部調用Webpack。它的優點是提供了額外的能力——相似能夠刷新瀏覽器的「Live Reloading」,以及(或者)局部更新模塊的**「模塊熱重載」功能(HMR)**。
使用方法:
OPTION 1:
//全局安裝
npm install webpack-dev-server --save
//終端使用
$ webpack-dev-server --inline --hot
OPTION 2:
//添加到package.json的script內
「scripts」: {
「start」: 「webpack-dev-server --inline --hot」,
...
}
//輸入下列命令行進行使用
$ npm start
瀏覽器打開下列地址
http://localhost:8080
複製代碼
值得注意的是,有一些選項好比「inline」和「hot」僅用於webpack-dev-server,而好比「hide-modules」僅用於CLI。
另一件須要知道的事情是你能夠經過兩種方式對webpack-dev-server進行配置:
webpack.config.js
的「devServer」對象。//使用CLI
webpack-dev-server --hot --inline
//使用webpack.config.js
devServer: {
inline: true,
hot:true
}
複製代碼
我發現有時候devServer的配置並無論用!因此我更傾向於把這些選項以CLI的方式寫入
package.json
裏面:
//package.json
{
scripts:
{「start」: 「webpack-dev-server --hot --inline」}
}
複製代碼
注意:確保你木有把
hot:true
和-hot
寫在一起。
「inline」選項爲整個頁面提供了「Live reloading」功能。「hot」選項提供了「模塊熱重載」功能,它會嘗試僅僅更新組件被改變的部分(而不是整個頁面)。若是咱們把這兩個選項都寫上,那麼當文件被改動時,webpack-dev-server會先嚐試HMR,若是這無論用,它就會從新加載整個頁面。
//當文件被改動後,下面的三個選項都會生成新的bundle,可是,
//1. 頁面不會刷新
$ webpack-dev-server
//2. 刷新整個頁面
$ webpack-dev-server --inline
//3. 僅僅刷新被改動的部分(HMR),若是HMR失敗則刷新整個頁面
$ webpack-dev-server --inline --hot
複製代碼
entry告訴Webpack入口文件或者起點在哪裏。它能夠是一個字符串,一個數組或者一個對象。這可能會使你感到困惑,但不一樣的類型適用於不一樣的場合。
若是你使用的是單個起點(大部分項目都是如此),那麼你可使用任意的類型,它們的結果都會是同樣的。
可是,若是你想要添加互不依賴的多個文件,你可使用數組的格式。
舉個栗子,你的HTML可能須要「googleAnalytics.js」。你能夠告訴Webpack在bundle.js的後面把它添加進去:
如今,當你有一個包含多個HTML文件的多頁應用,而不是單頁應用的項目的時候(index.html和profile.html),你能夠經過對象格式告訴Webpack去一次性生成多個bundle文件。
下面的配置會生成兩個JS文件:indexEntry.js
和profileEntry.js
,你能夠在index.html
和profile.html
分別使用它們:
使用方法:
//profile.html
<script src=」dist/profileEntry.js」></script>
//index.html
<script src=」dist/indexEntry.js」></script>
複製代碼
注意:文件名來自「entry」對象的key。
你也能夠在entry對象中使用數組。下面的例子會生成三個文件:一個包含三個文件的vendor.js
,一個index.js
和一個profile.js
。
output告訴Webpack應該在哪裏以怎樣的方式去放置打包好的文件。它有兩個屬性:「path」和「publicPath」,這也許會對用戶形成必定的困惑。
「path」會簡單地告訴Webpack生成文件輸出位置。「publicPath」多被一些Webpack的插件使用,在HTML文件以生產環境方式被構建的時候,更新CSS文件內的URL地址。
舉個栗子,在你的CSS文件裏面,你可能會在URL裏面加載./test.png
。可是在生產環境中,test.png
極可能放在CDN內——好比當你的node.js服務器運行在Heroku的時候。這意味着,你可能在生產環境內不得不手動更新文件內的URL指向。
相反,你可使用Webpack的publicPath
以及其餘適用於這個屬性的插件在生產環境中自動地更新文件內部的URL指向。
//開發環境:服務器和圖片都放在本地
.image {
background-image: url(‘./test.png’);
}
//生產環境:服務器在Heroku而圖片在CDN
.image {
background-image: url(‘https://someCDN/test.png’);
}
複製代碼
加載器是額外的node模塊,用於「加載」或者「引入」不一樣類型的文件,並把他們轉化成瀏覽器可以識別的格式——好比JS文件、內聯樣式表或其餘格式。另外,加載器也容許以「require」或者ES6的「import」的方式把這些文件引入到JS文件當中。
例如,你可使用babel-loader
把ES6代碼轉化成ES5代碼:
module: {
loaders: [{
test: /\.js$/, // 判斷文件格式,若爲「.js」文件則調用loader
exclude: /node_modules/, // 排除node_modules文件夾
loader: ‘babel’ // 使用babel(babel-loader的縮寫)
}]
複製代碼
不一樣的加載器能夠鏈式地在針對同一個文件類型進行工做。鏈式加載器的工做順序是從右到左的,而且經過「!」分割。
舉個栗子,咱們有一個叫作myCssFile.css
的CSS文件,如今想把它以<style></style>
的方式在咱們的HTML文件中使用,能夠經過兩個加載器去完成這個需求:css-loader
和style-loader
。
module: {
loaders: [{
test: /\.css$/,
loader: ‘style!css’ // style-loader!css-loader的縮寫
}]
複製代碼
這是運行原理:
require(myCssFile.css)
,若是有這句話而且找到了這個依賴,它會首先把這個文件交給css-loader
。css-loader
加載全部的CSS文件及其依賴包(例如經過@import
引入的其餘CSS文件)到一個JSON文件中。隨後Webpack會把結果交給style-loader
。style-loader
拿到這個JSON文件並把它注入到<style></style>
標籤當中,並把這個標籤添加到index.html
文件內。加載器其自身能夠經過配置不一樣的參數實現不一樣的功能。
在下面的例子中,咱們配置了url-loader
,當圖片小於1024byte的時候使用DataURL,當圖片大雨1024byte的時候使用URL。咱們經過下面兩個傳入limit
參數的方法來實現這個功能:
babel-loader
使用presets
去規定如何把ES6代碼轉化爲ES5代碼,以及如何把React的JSX轉化爲JS。咱們能夠經過query
方法進行配置:
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['react', 'es2015']
}
}
]
}
複製代碼
然而,在許多項目中babel的配置項會很是巨大。因此做爲替代,你能夠把這些配置項寫入一個叫作.babelrc
的文件中。若是這個文件存在的話bable-loader
會自動的加載這個文件。
因此在許多例子中,你會看到:
//webpack.config.js
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel'
}
]
}
//.bablerc
{
「presets」: [「react」, 「es2015」]
}
複製代碼
插件是額外的node模塊,多用於處理輸出文件。
例如,uglifyJSPlugin
會壓縮並混淆JS代碼,使其體積減少。
一樣的,extract-text-webpack-plugin
會在內部使用css-loader
和style-loader
去把全部的CSS合併爲一個文件,而且最終把結果提取到一個分離在外部的style.css
文件中,最後在index.html
中引用這個CSS文件。
//webpack.config.js
//Take all the .css files, combine their contents and it extract them to a single "styles.css"
var ETP = require("extract-text-webpack-plugin");
module: {
loaders: [
{test: /\.css$/, loader:ETP.extract("style-loader","css-loader") }
]
},
plugins: [
new ExtractTextPlugin("styles.css") //Extract to styles.css file
]
}
複製代碼
注意,若是你只打算把CSS以行內樣式的形式在HTML中引用,你能夠僅僅使用css-loader
和style-loader
,像下面的例子:
module: {
loaders: [{
test: /\.css$/,
loader: ‘style!css’ // style-loader!css-loader的縮寫
}]
複製代碼
正如你可能已經弄明白的,加載器在單個文件的程度上,在打包結束以前或者打包的過程當中運行。
而插件是在打包或者數據塊的程度上,在輸出打包文件的過程當中進行運做。一些插件好比commonsChunksPlugins甚至在更早的階段開始運做,能夠用來修改打包的方式。
一些Webpack配置文件帶有解析擴展文件名的屬性,它們像下面的例子同樣,包含了一些空字符串。這些空字符串被用於幫助加載一些沒有擴展名的文件,好比require("./myJSFile")
或者import myJSFile from './myJSFile'
。
{
resolve: {
extensions: [‘’, ‘.js’, ‘.jsx’]
}
}
複製代碼
全文完。
感謝Tobias Koppers(Webpack的做者)幫我審閱這篇文章!
感謝你的閱讀。我是Jrain,歡迎關注個人專欄,將不按期分享本身的學習體驗,開發心得,搬運牆外的乾貨。下次見啦!