什么是 webpack?
webpack是近期最火的一款模塊加載器兼打包工具,它能把各種資源,例如JS(含JSX)、coffee、樣式(含less/sass)、圖片等都作為模塊來使用和處理。
我們可以直接使用 require(XXX) 的形式來引入各模塊,即使它們可能需要經(jīng)過編譯(比如JSX和sass),但我們無須在上面花費(fèi)太多心思,因?yàn)?webpack 有著各種健全的加載器(loader)在默默處理這些事情,這塊我們后續(xù)會(huì)提到。
你可以不打算將其用在你的項(xiàng)目上,但沒有理由不去掌握它,因?yàn)橐越?Github 上各大主流的(React相關(guān))項(xiàng)目來說,它們倉(cāng)庫(kù)上所展示的示例已經(jīng)是基于 webpack 來開發(fā)的,比如 React-Boostrap 和 Redux
webpack 的優(yōu)勢(shì)
1. webpack 是以 commonJS 的形式來書寫腳本滴,但對(duì) AMD/CMD 的支持也很全面,方便舊項(xiàng)目進(jìn)行代碼遷移。
2. 能被模塊化的不僅僅是 JS 了。
3. 開發(fā)便捷,能替代部分 grunt/gulp 的工作,比如打包、壓縮混淆、圖片轉(zhuǎn)base64等。
4. 擴(kuò)展性強(qiáng),插件機(jī)制完善,特別是支持 React 熱插拔(見react-hot-loader)的功能讓人眼前一亮。
我們談?wù)劦谝稽c(diǎn)。以 AMD/CMD 模式來說,鑒于模塊是異步加載的,所以我們常規(guī)需要使用 define 函數(shù)來幫我們搞回調(diào):
define(['package/lib'],function(lib){
functionfoo(){
lib.log('hello world!');
}
return{
foo: foo
};
});
另外為了可以兼容 commonJS 的寫法,我們也可以將 define 這么寫:
define(function(require, exports, module){
varsomeModule = require("someModule");
varanotherModule = require("anotherModule");
someModule.doTehAwesome();
anotherModule.doMoarAwesome();
exports.asplode =function(){
someModule.doTehAwesome();
anotherModule.doMoarAwesome();
};
});
然而對(duì) webpack 來說,我們可以直接在上面書寫 commonJS 形式的語(yǔ)法,無須任何 define (畢竟最終模塊都打包在一起,webpack 也會(huì)最終自動(dòng)加上自己的加載器):
varsomeModule = require("someModule");
varanotherModule = require("anotherModule");
someModule.doTehAwesome();
anotherModule.doMoarAwesome();
exports.asplode =function(){
someModule.doTehAwesome();
anotherModule.doMoarAwesome();
};
安裝和配置
一. 安裝
我們常規(guī)直接使用 npm 的形式來安裝:
$ npm install webpack -g
當(dāng)然如果常規(guī)項(xiàng)目還是把依賴寫入 package.json 包去更人性化:
$ npm init
$ npm install webpack --save-dev
二. 配置
每個(gè)項(xiàng)目下都必須配置有一個(gè) webpack.config.js ,它的作用如同常規(guī)的 gulpfile.js/Gruntfile.js ,就是一個(gè)配置項(xiàng),告訴 webpack 它需要做什么。
varwebpack = require('webpack');
varcommonsPlugin =newwebpack.optimize.CommonsChunkPlugin('common.js');
module.exports = {
//插件項(xiàng)
plugins: [commonsPlugin],
//頁(yè)面入口文件配置
entry: {
index :'./src/js/page/index.js'
},
//入口文件輸出配置
output: {
path:'dist/js/page',
filename:'[name].js'
},
module: {
//加載器配置
loaders: [
{ test: /\.css$/, loader:'style-loader!css-loader'},
{ test: /\.js$/, loader:'jsx-loader?harmony'},
{ test: /\.scss$/, loader:'style!css!sass?sourceMap'},
{ test: /\.(png|jpg)$/, loader:'url-loader?limit=8192'}
]
},
//其它解決方案配置
resolve: {
root:'E:/github/flux-example/src',//絕對(duì)路徑
extensions: ['','.js','.json','.scss'],
alias: {
AppStore :'js/stores/AppStores.js',
ActionType :'js/actions/ActionType.js',
AppAction :'js/actions/AppAction.js'
}
}
};
⑴ plugins 是插件項(xiàng),這里我們使用了一個(gè) CommonsChunkPlugin 的插件,它用于提取多個(gè)入口文件的公共腳本部分,然后生成一個(gè) common.js 來方便多頁(yè)面之間進(jìn)行復(fù)用。
⑵ entry 是頁(yè)面入口文件配置,output 是對(duì)應(yīng)輸出項(xiàng)配置(即入口文件最終要生成什么名字的文件、存放到哪里),其語(yǔ)法大致為:
{
entry: {
page1:"./page1",
//支持?jǐn)?shù)組形式,將加載數(shù)組中的所有模塊,但以最后一個(gè)模塊作為輸出
page2: ["./entry1","./entry2"]
},
output: {
path:"dist/js/page",
filename:"[name].bundle.js"
}
}
該段代碼最終會(huì)生成一個(gè) page1.bundle.js 和 page2.bundle.js,并存放到 ./dist/js/page 文件夾下。
⑶ module.loaders 是最關(guān)鍵的一塊配置。它告知 webpack 每一種文件都需要使用什么加載器來處理:
module: {
//加載器配置
loaders: [
//.css 文件使用 style-loader 和 css-loader 來處理
{ test: /\.css$/, loader:'style-loader!css-loader'},
//.js 文件使用 jsx-loader 來編譯處理
{ test: /\.js$/, loader:'jsx-loader?harmony'},
//.scss 文件使用 style-loader、css-loader 和 sass-loader 來編譯處理
{ test: /\.scss$/, loader:'style!css!sass?sourceMap'},
//圖片文件使用 url-loader 來處理,小于8kb的直接轉(zhuǎn)為base64
{ test: /\.(png|jpg)$/, loader:'url-loader?limit=8192'}
]
}
如上,"-loader"其實(shí)是可以省略不寫的,多個(gè)loader之間用“!”連接起來。
注意所有的加載器都需要通過 npm 來加載,并建議查閱它們對(duì)應(yīng)的 readme 來看看如何使用。
拿最后一個(gè)url-loader來說,它會(huì)將樣式中引用到的圖片轉(zhuǎn)為模塊來處理,使用該加載器需要先進(jìn)行安裝:
npm install url-loader -save-dev
配置信息的參數(shù)“?limit=8192”表示將所有小于8kb的圖片都轉(zhuǎn)為base64形式(其實(shí)應(yīng)該說超過8kb的才使用 url-loader 來映射到文件,否則轉(zhuǎn)為data url形式)。
⑷ 最后是 resolve 配置,這塊很好理解,直接寫注釋了:
resolve: {
//查找module的話從這里開始查找
root:'E:/github/flux-example/src',//絕對(duì)路徑
//自動(dòng)擴(kuò)展文件后綴名,意味著我們r(jià)equire模塊可以省略不寫后綴名
extensions: ['','.js','.json','.scss'],
//模塊別名定義,方便后續(xù)直接引用別名,無須多寫長(zhǎng)長(zhǎng)的地址
alias: {
AppStore :'js/stores/AppStores.js',//后續(xù)直接 require('AppStore') 即可
ActionType :'js/actions/ActionType.js',
AppAction :'js/actions/AppAction.js'
}
}