5分鐘打造簡(jiǎn)易高效的webpack常用配置
webpack給前端開(kāi)發(fā)帶來(lái)了毋庸置疑的改變,它把JS,圖片,css都作為模塊處理,同時(shí)具有開(kāi)發(fā)便捷,自動(dòng)化,兼容AMD寫法等等諸多無(wú)須贅述的優(yōu)點(diǎn),更令人稱道的是其插件社區(qū)非常強(qiáng)大,對(duì)于不同的業(yè)務(wù)需求和技術(shù)需求社區(qū)都有大量插件可供使用。
凡事都具有兩面性,許多人說(shuō):前端開(kāi)發(fā)再也不能只需新建HTML文件和JS文件就可以開(kāi)始寫代碼了。webpack帶來(lái)了更高級(jí)更規(guī)范的前端開(kāi)發(fā)模式,由于其本身也在不斷完善中,從1到2再到發(fā)布不久的webpack3,頻繁的修改給新手帶來(lái)了許多困惑。而且網(wǎng)絡(luò)上各種教程魚目混雜,經(jīng)常出現(xiàn)別人的教程代碼copy下來(lái)在自己的環(huán)境卻跑不通的蛋疼問(wèn)題。就拿 devtool 配置項(xiàng)來(lái)說(shuō),官方文檔提供了多達(dá)7種的配置方法,連react核心團(tuán)隊(duì)成員Pete Hunt都在twitter上調(diào)侃:我分不清webpack的許多配置之間的區(qū)別。所以今天我們拋開(kāi)那些琳瑯滿目的插件和令人煩躁的配置項(xiàng),筆者和大家一起5分鐘從零搭建一個(gè)簡(jiǎn)易高效的webpack開(kāi)發(fā)環(huán)境。
首先我們明確一下需求:
- 打包調(diào)試
- 提取公共代碼
- 壓縮
- 熱替換
1.打包調(diào)試
第一步,我們?cè)谀繕?biāo)文件夾下安裝webpack(假設(shè)已有 package.json )
npm i webpack@ -g cnpm i webpack@ --save-dev
(這里推薦大家安裝穩(wěn)定的2.x版本)
項(xiàng)目結(jié)構(gòu)如圖:
我們將編寫的js代碼和樣式文件放置在 app 文件夾內(nèi)(正常項(xiàng)目開(kāi)發(fā)需要 js 文件和 less文件更規(guī)范的組織文件結(jié)構(gòu),此處僅為演示方便)。
第二步,我們?cè)谀繕?biāo)文件夾下新建 webpack.config.js
module.exports = {
entry:{
main:__dirname + '/app/main.js',
},
output:{
path:__dirname + '/public',
filename:'[name].[id].js',//此格式寫法后續(xù)會(huì)提到為什么
publicPath:'/public/'
}
}
我們已經(jīng)完成了webpack最基礎(chǔ)的部分:添加了文件的輸入和輸出。入口是 app 文件夾內(nèi)的 main.js 文件,出口為 public 文件夾。接下來(lái)我們來(lái)處理各種文件的解析,就是大名鼎鼎的 loader 的舞臺(tái)了。假設(shè)我們使用 es6 和 less 開(kāi)發(fā),那么我們需要:
npm i babel-loader babel-core babel-preset-es2015 babel-preset-stage-0 --save-dev npm i less less-loader css-loader style-loader --save-dev
接下來(lái)我們只需要在 modules 字段下把這些 loader 加進(jìn)去:
module.exports = {
devtool:'cheap-module-eval-source-map',//多種選擇,選擇最適合自己的
entry:{
main:__dirname + '/app/main.js',
},
output:{
path:__dirname + '/public',
filename:'[name].[id].js',
publicPath:'/public/'
},
module:{
loaders:[
{
test:/\.js$/, //解析文件類型
exclude:/node_modules/, //排除node_modules文件
loader:'babel-loader', //使用哪種loader解析
query:{
presets:['es2015','stage-0']//loader的配置項(xiàng),解析es6
}
},
{
test:/\.less$/,
exclude:/node_modules/,
loader:'style-loader!css-loader!less-loader'//順序?yàn)閺挠蚁蜃?
}
]
},
}
大功告成!
如果你在全局安裝有webpack的話,可以在終端敲入webpack并回車,幾秒鐘后, main.js 文件已經(jīng)在 public 打包出來(lái)了!
之后我們?cè)?index.html 中引入 main.0.js 文件,再打開(kāi) index.html 就可以看到效果了。
以上步驟,我們已經(jīng)實(shí)現(xiàn)了文件的打包調(diào)試,但是現(xiàn)在有個(gè)問(wèn)題擺在我們面前:第三方庫(kù)代碼和業(yè)務(wù)代碼打包到了同一個(gè)文件 main.0.js 內(nèi),每次更新代碼都要更新整個(gè)文件。那么接下來(lái)我們對(duì)代碼進(jìn)行拆分。
2.提取公共代碼
引入 CommonsChunkPlugin 插件,在 webpack.config.js 添加如下內(nèi)容:
module.exports = {
devtool:'cheap-module-eval-source-map',
entry:{
main:__dirname + '/app/main.js',
vendor:'moment'
},
output:{
path:__dirname + '/public',
filename:'[name].[id].js',
publicPath:'/public/'
},
module:{
loaders:[
{
test:/\.js$/,
exclude:/node_modules/,
loader:'babel-loader',
query:{
presets:['es2015','stage-0']
}
},
{
test:/\.less$/,
exclude:/node_modules/,
loader:'style-loader!css-loader!less-loader'
}
]
},
plugins:[
new webpack.optimize.CommonsChunkPlugin({
names:['vendor','manifest']
})
]
}
我們看到向插件的構(gòu)造函數(shù)傳入了兩個(gè)參數(shù) vendor 和 manifest ,以及我們?cè)?entry 也加入了新的入口 moment 。 moment 是常用的時(shí)間處理的第三方庫(kù),我們可以通過(guò) npm i moment --save-dev 進(jìn)行安裝。而 entry 處的 vendor 將成為 output 字段 filename 中 [name] 的值,也就是說(shuō)將打包出 main.x.js 和 vendor.x.js 兩個(gè)文件, main.x.js 文件將保存我們的業(yè)務(wù)代碼, vendor.x.js 將保存 moment 的代碼,這樣我們將公共代碼和業(yè)務(wù)代碼進(jìn)行了初步分離。
在新添加的 CommonmChunkPlugin 插件中,我們添加了 manifest 值,這是為什么呢?如果你不添加這個(gè)值,你在打包時(shí)會(huì)發(fā)現(xiàn), main.x.js 有更新, vendor.x.js 還是有更新,并未真正實(shí)現(xiàn)"分離"。官方文檔對(duì)此的解釋是:
The issue here is that on every build, webpack generates some webpack runtime code, which helps webpack do it's job. When there is a single bundle, the runtime code resides in it. But when multiple bundles are generated, the runtime code is extracted into the common module, here the vendor file.
大致的意思就是說(shuō),webpack每次編譯時(shí)運(yùn)行的代碼會(huì)影響到 hash 值的變化,當(dāng)只有一個(gè)打包文件時(shí)這部分代碼會(huì)塞進(jìn)去,當(dāng)有多個(gè)打包文件時(shí),這部分代碼會(huì)進(jìn)入公共的 vendor 。所以解決辦法是使用 manifest 字段把這部分代碼從 vendor 中作為一個(gè)公共模塊抽出來(lái),從而不會(huì)影響 vendor 。
將以上的配置寫入 webpack.config.js ,運(yùn)行webpack命令,我們發(fā)現(xiàn)業(yè)務(wù)代碼和公共庫(kù)代碼成功分離,改寫 main.1.js 文件的內(nèi)容,再次打包,發(fā)現(xiàn) vendor 文件并沒(méi)有變化,成功!
當(dāng)我們?cè)龠M(jìn)行打包時(shí),發(fā)現(xiàn)又會(huì)多出了新的 main.x.js 等文件,打包三次就會(huì)出現(xiàn)三個(gè) main.x.js 文件,此時(shí)該怎么辦呢?我們可以使用 clean-webpack-plugin 插件:
npm i clean-webpack-plugin --save-dev
然后在 webpack.config.js 中引入:
var CleanWebpackPlugin = require('clean-webpack-plugin');
new CleanWebpackPlugin(
['public/main.*.js','public/manifest.*.js'],//要?jiǎng)h除的文件目錄匹配
{
root:__dirname,
verbose:true,
dry:false
}
),
這樣我們每次在打包新的代碼時(shí),舊文件就會(huì)刪除,不會(huì)再出現(xiàn)同一份文件存在多份的情況。
3.壓縮
在webpack中,圖片,css,js等等其他資源皆可壓縮,本文僅以壓縮js為例。
安裝插件:
npm i uglifyjs-webpack-plugin --save-dev
在 webpack.config.js 中引入:
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
new UglifyJsPlugin({
beautify:true,
exclude:['/node_modules/'],
compress:{
warnings:false
},
output:{
comments:false
}
})
我們指定了壓縮的方法,排除了不需要壓縮的 node_modules 部分,同時(shí)我們?nèi)コ?comments 部分( comments 為@license等注釋,是可觀的壓縮空間)。再次在終端輸入打包命令,可見(jiàn)js打包后的體積有令人滿意的減小。
4.熱替換
webpack總是繞不開(kāi)熱替換的話題。熱替換的功能配置和原理是一大話題,三天三夜也說(shuō)不完,也并非本文重點(diǎn),本文只提供簡(jiǎn)易高效的配置方法。
熱替換存在兩種使用方式, cli 和 node 。 cli 方式無(wú)需添加新的熱替換插件,且無(wú)需在入口處添加 webpack-dev-server 等入口,故本文采用 cli 使用方式。
在 webpack.config.js 中添加 devServer 字段,加入如下代碼:
devServer:{
inline:true,
hot:true
},
保存后運(yùn)行 webpack-dev-server --inline --hot --progress ,再修改下 main.less 文件的樣式,會(huì)發(fā)現(xiàn)瀏覽器并沒(méi)有刷新,但頁(yè)面已經(jīng)發(fā)生了變化,我們的熱替換功能也成功加入了!
tips:
在實(shí)際項(xiàng)目打包時(shí),可以將 filename 字段的值換為 [name].[chunkhash].js ,其中 [chunkhash] 為webpack每次打包后給每個(gè)模塊的標(biāo)識(shí)值,這個(gè)值每次打包后都會(huì)更換。為什么在此處我們使用 [id] 呢,因?yàn)?chunkhash 與熱替換存在沖突,終端會(huì)有報(bào)錯(cuò),那么使用 id 可以算作一個(gè)解決方案。這就引申出另一話題,我們可以使用兩套webpack配置分別用于生產(chǎn)環(huán)境和開(kāi)發(fā)環(huán)境,通過(guò)webpack指定config來(lái)進(jìn)行打包。例如我們?cè)陂_(kāi)發(fā)環(huán)境使用 id ,在生產(chǎn)環(huán)境去掉熱替換并使用 hash 的方式。而且,一些壓縮插件也沒(méi)必要在開(kāi)發(fā)環(huán)境過(guò)度使用,兩套配置能讓webpack發(fā)揮最大的威力。
另外, chunkhash 和 hash 有區(qū)別, chunkhash 顧名思義是模塊的標(biāo)識(shí),而 hash 是webpack每次編譯的標(biāo)識(shí)值,不同的資源如js和css存在 chunkhash 解耦的問(wèn)題,此處不進(jìn)行過(guò)多討論。
5.運(yùn)行
我們知道,每次打包后,都會(huì)有新的 main.x.js 文件生成,其hash值每次打包后都會(huì)發(fā)生變化,難道我們的 index.html 文件需要每次打包后都手動(dòng)修改 main.x.js 的路徑嗎?還好社區(qū)提供了 html-webpack-plugin 插件,可以在已有html模板的條件下自動(dòng)為我們生成帶有最新代碼的html文件:
npm i html-webpack-plugin --save-dev
在 webpack.config.js 中引入:
var HtmlWebpackPlugin = require('html-webpack-plugin');
new HtmlWebpackPlugin({
title:'demo',
template:'index.html'
}),
在終端運(yùn)行打包命令,我們看到 public 文件夾下生成了新的 index.html 文件:
以后我們?cè)龠M(jìn)行調(diào)試時(shí),以本文為例,則需要打開(kāi) localhost:8080/public/index.html ,因?yàn)槊看蝫ebpack的 HtmlWebpackPlugin 都會(huì)把新的js文件加入到這個(gè)html文件內(nèi)。在開(kāi)發(fā)全部完成后,我們可以將js路徑寫死,添加到原有的 index.html 文件中。
以下是我們 webpack.config.js 全部的配置;
var webpack = require('webpack');
var CleanWebpackPlugin = require('clean-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
devtool:'cheap-module-eval-source-map',
entry:{
main:__dirname + '/app/main.js',
vendor:'moment'
},
output:{
path:__dirname + '/public',
filename:'[name].[id].js',
publicPath:'/public/'
},
devServer:{
inline:true,
hot:true
},
module:{
loaders:[
{
test:/\.js$/,
exclude:/node_modules/,
loader:'babel-loader',
query:{
presets:['es2015','stage-0']
}
},
{
test:/\.less$/,
exclude:/node_modules/,
loader:'style-loader!css-loader!less-loader'
}
]
},
plugins:[
new CleanWebpackPlugin(
['public/main.*.js','public/manifest.*.js'],
{
root:__dirname,
verbose:true,
dry:false
}
),
new webpack.optimize.CommonsChunkPlugin({
names:['vendor','manifest']
}),
new HtmlWebpackPlugin({
title:'demo',
template:'index.html'
}),
new UglifyJsPlugin({
beautify:true,
exclude:['/node_modules/'],
compress:{
warnings:false
},
output:{
comments:false
}
})
]
}
整個(gè)項(xiàng)目,我們?cè)?app 文件下的 main.js 內(nèi)寫業(yè)務(wù)代碼, main.less 寫樣式,在 public/index.html 下使用熱替換進(jìn)行調(diào)試,打包后的壓縮文件在 public 文件夾下,并且對(duì)業(yè)務(wù)代碼,第三方代碼進(jìn)行了清晰地區(qū)分。
使用這份webpack配置,我們實(shí)現(xiàn)了:
- 工程的打包調(diào)試
- 公共代碼提取,提高開(kāi)發(fā)效率
- 資源壓縮
- 熱替換
這份配置麻雀雖小,五臟俱全。本文還有許多不完善之處,比如一些插件的使用方法,原理沒(méi)有與大家講清楚,但webpack實(shí)在太龐大了,一個(gè)插件的使用方法和原理都可以寫上千字的文章了,學(xué)習(xí)不可淺嘗輒止,但也不能太鉆牛角尖,與大家共勉~
- webpack常用配置項(xiàng)配置文件介紹
- 深入理解Webpack 中路徑的配置
- webpack教程之webpack.config.js配置文件
- vue-cli的webpack模板項(xiàng)目配置文件分析
- webpack引入eslint配置詳解
- webpack多入口文件頁(yè)面打包配置詳解
- Vue + Webpack + Vue-loader學(xué)習(xí)教程之相關(guān)配置篇
- 詳解vue2.0腳手架的webpack 配置文件分析
- 詳解vue-cli + webpack 多頁(yè)面實(shí)例配置優(yōu)化方法
- 詳解webpack之scss和postcss-loader的配置
- webpack配置sass模塊的加載的方法
- webpack構(gòu)建vue項(xiàng)目的詳細(xì)教程(配置篇)
- webpack配置導(dǎo)致字體圖標(biāo)無(wú)法顯示的解決方法
- webpack3+React 的配置全解
- 淺談在vue中用webpack打包之后運(yùn)行文件的問(wèn)題以及相關(guān)配置方法
- 詳解Webpack + ES6 最新環(huán)境搭建與配置
- webpack配置打包后圖片路徑出錯(cuò)的解決
- webpack高級(jí)配置與優(yōu)化詳解
相關(guān)文章
Javascript 完美運(yùn)動(dòng)框架(逐行分析代碼,讓你輕松了運(yùn)動(dòng)的原理)
這篇文章主要介紹了Javascript 完美運(yùn)動(dòng)框架,逐行分析代碼,讓你輕松了運(yùn)動(dòng)的原理,需要的朋友可以參考下2015-01-01
javascript實(shí)現(xiàn)checkbox復(fù)選框?qū)嵗a
這篇文章主要為大家介紹了javascript實(shí)現(xiàn)checkbox復(fù)選框?qū)嵗a,對(duì)checkbox復(fù)選框進(jìn)行美化,感興趣的小伙伴們可以參考一下2016-01-01
Javascript中使用parseInt函數(shù)需要注意的問(wèn)題
這篇文章主要介紹了Javascript中使用parseInt函數(shù)需要注意的問(wèn)題,本文講解了parseInt函數(shù)在IE8下可能會(huì)返回0值的兼容問(wèn)題解決方法,需要的朋友可以參考下2015-04-04
JavaScript利用正則表達(dá)式替換字符串中的內(nèi)容
本文主要介紹了JavaScript利用正則表達(dá)式替換字符串中內(nèi)容的具體實(shí)現(xiàn)方法,并做了簡(jiǎn)要注釋,便于理解。具有一定的參考價(jià)值,需要的朋友可以看下2016-12-12
js中switch case循環(huán)實(shí)例代碼
這篇文章主要介紹了js中switch case循環(huán)實(shí)例代碼,有需要的朋友可以參考一下2013-12-12
javascript簡(jiǎn)單實(shí)現(xiàn)深淺拷貝過(guò)程詳解
這篇文章主要介紹了javascript簡(jiǎn)單實(shí)現(xiàn)深淺拷貝過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10

