本需要對webpack已有一定的了解,如果你沒接觸過webpack或者剛剛接觸webpack,可以考慮先看一下我的這篇教程。
##1.打包多文件 之前,當需要打包多個而文件時,我是這么寫的: ```javascript module.exports={ entry:{ "page/main/main":'./src/js/index.js', "page/car/car":"./src/js/car.js", "page/goods/goods":"./src/js/goods.js", }, output:{ filename:"[name].[hash].js", path:path.resolve(__dirname,'dist') }, plugins:[ new webpack.HotModuleReplacementPlugin(), new htmlWebpackPlugin({ title:"首頁", template:'./src/index.html', filename:"index.html", chunks:["page/main/main"] }), new htmlWebpackPlugin({ title:"購物車", template:'./src/index.html', filename:"car.html", chunks:["page/car/car"] }), new htmlWebpackPlugin({ title:"商品", template:'./src/index.html', filename:"goods.html", chunks:["page/goods/goods"] }) ] ```
可以發現,我們在entry中手動引入了多個文件; 在plugins中,我們又手動的多次new htmlWebpackPlugin()。
雖然能夠正常的運行,然而存在很多限制。
-
首先:存在大量重復的代碼。現在只有3個頁面,假如現有10個頁面,那么你就得手動的new10次,在enrty中輸入10個入口。
-
其次:當增加新的頁面時,又得過來webpack的配置里面手動的再加入到入口文件中。
明顯的,對於這些重復的而且長得很像的,如果能自動獲取到需要配置的文件,並且在一個循環里面調用,那就方便多了。
使用node.js的golb模塊來獲取文件
//引入glob
var glob= require('glob');
//同步讀取src目錄下所有的html文件
var files = glob.sync('./src/*.html');
var entry={};
var plugins=[];
//循環將文件
files.forEach(function(item,i){
//item類似:./src/index.html
var htmlName=item.slice(item.lastIndexOf("/")+1);
//最后生成的文件名只需要最后面的名字index.html
var name=htmlName.split(".")[0];
//添加到entry入口,並制定生成文件的目錄
entry["page/"+name+"/"+name]='./src/js/'+name+'.js'
//生成htmlWebpackPlugin實例
plugins.push(
new htmlWebpackPlugin({
template:item,
filename:htmlName,
chunks:["page/"+name+"/"+name]
})
)
});
module.exports={
entry:entry,
output:{
filename:"[name].[chunkhash].js",
path:path.resolve(__dirname,'dist'),
},
module:{
rules:[
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
]
},
plugins:plugins
}
可以發現,使用glob.sync來讀取文件並進行循環操作,原本需要手動操作的部分,循環已經幫我們處理好了。原本需要很多new new htmlWebpackPlugin()已經不再存在,而且即使后續需要添加新的頁面,也不需要我們手動去添加,glob.sync會自己去讀取所有的*.html文件,接着循環會幫我們處理。
這里需要注意的地方就是,index.html對應的js文件必須為index.js,ceshi.html文件對應的文件必須為ceshi.js。至於js里面需要引入其他庫,或者自己寫的工具函數,哪個頁面需要,只需引入即可,webpack會自己處理好。
2.使用DLL提取公共庫
當頁面存在引用相同的庫時,最好還是將這些庫文件提取到單獨的文件,頁面只保留業務邏輯的js代碼。
為了提取文件,有2中處理方案:
- 使用CommonsChunkPlugin來提取
在上面的代碼上增加:(以下這部分代碼只在使用CommonsChunkPlugin時需要)
entry.vendor=[
//舉例如下:
"vue","vuex","axios","jquery","vue-router","moment","lodash"
];
plugins=[
new webpack.optimize.CommonsChunkPlugin({
name:["vendor","manifest"]
})
]
//修改循環中的chunks, 增加vendor以及manifest:
files.forEach(function(item,i){
plugins.push(
new htmlWebpackPlugin({
template:item,
filename:htmlName,
chunks:["page/"+name+"/"+name,"vendor","mainfest"]
})
)
});
再執行webpack,可以發現公共的庫已經被提取到單獨的vendor文件。
- 使用DLL
對於 CommonsChunkPlugin
,webpack 每次打包實際還是需要去處理這些第三方庫,只是打包完之后,能把第三方庫和我們自己的代碼分開。
而DLLPlugin
則是能把第三方代碼完全分離開,即每次只打包項目自身的代碼。
這里我使用的是autodll-webpack-plugin。
//安裝模塊
npm install --save-dev autodll-webpack-plugin
//引入模塊
var AutoDllPlugin = require('autodll-webpack-plugin');
在plugins中添加插件:(需要放在循環之前,防止數組被重新賦值)
var plugins=[
new AutoDllPlugin({
//文件名
filename: '[name].[hash].js',
//打包后的文件路徑
path: './page/common/',
//是否將生成的Js文件注入到html文件中
inject:true,
//需要分離的庫
entry: {
vendor: [
"vue","vuex","axios","jquery","vue-router","moment","lodash","vue-touch","vue-lazyload"
]
},
//壓縮代碼(注意這里是壓縮分離出來的庫文件代碼,跟外面的要區分開來,如果外面的需要壓縮,外面的plugins中也需要new一個壓縮實例)
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
})
]
執行webpack命令,可以發現公共的庫文件也被打包到單獨的文件了。
autodll-webpack-plugin
在內存中緩存了文件,所以當你第二此執行webpack命令進行打包時,可以明顯發現打包時間少了很多。
而CommonsChunkPlugin
每次都全部重新打包再進行分離,所以當需要多次修改文件時,autodll-webpack-plugin可以明顯降低打包的時間,尤其是依賴很多外部庫的時候。
3.使用webpack-merge區分生成環境和開發環境:
很多時候,我們都需要針對不同的環境進行不用的操作。
比如在生成環境下分離css到單獨文件:
var extractSass = new ExtractTextPlugin({
filename: "[name].[contenthash].css",
disable: process.env.NODE_ENV === "development"
});
在生成環境下要壓縮代碼:
new UglifyJSPlugin();
在開發環境下使用代理
devServer:{
proxy: {
'api': {
target: 'http://api.douban.com/v2/movie/',
secure: false,
changeOrigin: true
}
}
通常會用process.env.NODE_ENV === "development",並且在package.json中設置環境變量來進行判斷,不過當文件大了或者頁面需要判斷的地方多了之后,配置文件就會充斥着大量三元表達式
。
可以考慮用webpack-merge將配置文件拆分為3個文件,一個是webpack.common.js
,即不管是生產環境還是開發環境都會用到的部分,以及webpack.product.j
s和webpack.dev.js
, 並且使用webpack-merge來合並對象。
common.js
//webpack.common.js
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules:[
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'test'
})
],
}
dev.js
//開發環境webpack.dev.js
var merge = require('webpack-merge');
var common = require('./webpack.common.js');
module.exports = merge(common, {
module:{
rules:[
{
test: /\.css$/,
use:["style-loader","css-loader"]
}
]
},
devtool: 'inline-source-map',
devServer:{
open:true,
hot: true,
proxy: {
'/api/': {
target: 'http://baidu.com',
secure: false,
changeOrigin: true
}
}
},
})
product.js
//生產環境webpack.product.js
var merge = require('webpack-merge');
var UglifyJSPlugin = require('uglifyjs-webpack-plugin');
var common = require('./webpack.common.js');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var cleanPlugin = require("clean-webpack-plugin");
var extractSass = new ExtractTextPlugin({
filename: "[name].[contenthash].css",
});
module.exports = merge(common, {
module: {
rules: [{
test: /\.css$/,
use: extractSass.extract({
use: [{
loader: "css-loader"
}, ],
fallback: "style-loader"
})
}]
},
devtool: 'source-map',
plugins: [
new cleanPlugin(["dist"]),
new UglifyJSPlugin(),
extractSass,
]
});
在packjson中修改webpack默認配置文件
"scripts": {
"dev": "webpack-dev-server --config webpack.dev.js",
"build": "webpack --config webpack.product.js"
}
通過npm run dev以及npm run build來執行對應的操作。
以上只是一個簡單的示例,並不是一定需要拆分成3個文件,如果你的配置足夠簡單,寫成一個webpack.config.js足以。但是,假如你的項目足夠龐大,最好還是拆開來寫,可以參考vue-cli的腳手架的配置。