webpack-DllPlugin優化打包效能(基於vue-cli)
1.介紹
Dll這個概念應該是借鑑了Windows系統的dll。一個dll包,就是一個純純的依賴庫,它本身不能執行,是用來給你的app引用的。
打包dll的時候,Webpack會將所有包含的庫做一個索引,寫在一個manifest檔案中,而引用dll的程式碼(dll user)在打包的時候,只需要讀取這個manifest檔案,就可以了。
簡單說
將靜態資原始檔(執行依賴包)與原始檔分開打包,先使用DllPlugin給靜態資源打包,再使用DllReferencePlugin讓原始檔引用資原始檔。
2.預打包依賴模組
在根目錄建立一個webpack.dll.config.js
var path = require("path");
var webpack = require("webpack");
module.exports = {
// 你想要打包的模組的陣列
entry: {
vendor: ['vue', 'lodash', 'vuex', 'axios', 'vue-router', 'element-ui']
},
output: {
path: path.join(__dirname, './static/js'), // 打包後文件輸出的位置
filename: '[name].dll.js',
library: '[name]_library'
// vendor.dll.js中暴露出的全域性變數名。
// 主要是給DllPlugin中的name使用,
// 故這裡需要和webpack.DllPlugin中的`name: '[name]_library',`保持一致。
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, '.', '[name]-manifest.json'),
name: '[name]_library',
context: __dirname
} ),
// 壓縮打包的檔案,與該文章主線無關
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
]
};
重點:這裡引入的Dllplugin外掛,該外掛將生成一個manifest.json檔案,該檔案供webpack.config.js中加入的DllReferencePlugin使用,使我們所編寫的原始檔能正確地訪問到我們所需要的靜態資源(執行時依賴包)。
- path:manifest.json生成的資料夾及名字,該專案讓它生成在了根目錄下。
- name:和output. library保持一致即可。
- context:選填,manifest檔案中請求的上下文,預設為該webpack檔案上下文。(!!!我在學習這個外掛時一直沒有成功的坑點之一!!,這個上下文必須必須同webpack.config.js中DllReferencePlugin外掛的context所指向的上下文保持一致!!)
編寫完之後就可以預打包資原始檔了
## 以指定webpack檔案執行webpack打包
webpack --config ./webpack.dll.config.js
還可以把這句加到package.json裡面
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
"build": "cross-env NODE_ENV=production webpack --progress",
"dll": "webpack --config ./webpack.dll.config.js"
},
打包完成後,會在static檔案下面有個js檔案,其內有個vendor.dll.js,除此之外根目錄下還生成了vendor-manifest.json.現在我們已經不再需要將使用的那些包同源檔案一起打包了,但是這也需要在原始檔的webpack中配置DllReferencePlugin使用vendor-manifest.json來引用這個dll。
3.打包原始檔
這一步我們只需要改寫vue-cli為我們生成好的webpack.config.js即可:
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
// ...(省略未複製,並不是刪除了module裡的東西)
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
devServer: {
historyApiFallback: true,
noInfo: true
},
performance: {
hints: false
},
devtool: '#eval-source-map',
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./vendor-manifest.json')
})
]
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
該檔案裡主要是添加了plugins配置:
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./vendor-manifest.json')
})
]
context:與Dllplugin裡的context所指向的上下文保持一致,這裡都是指向了根目錄。
manifest:引入Dllplugin所生成的的manifest
最後
我們需要手動在根目錄的index.html裡引入所生成的dll庫。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>dll-test</title>
</head>
<body>
<div id="app"></div>
<script src="./static/js/vendor.dll.js"></script>
<script src="/dist/build.js"></script>
</body>
</html>
這裡也很講究,也是我之前失敗的原因之一!!我之前直接把的後面,導致一直報錯!其實這裡稍微動動腦筋就能明白,我們在main.js中引入的各種包,而main.js最終被打包為了build.js,那麼我們肯定要先把包引進來才能正確使用build.js啊!所以vendor.dll.js必須放在build.js之前引入。
其實還有一個問題
在webpack.config.js中:
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
這就代表main.js中的import Vue from ‘vue’ 其實是引用的’vue/dist/vue.esm.js’,而webpack.dll.config.js並不知道vue指代的是’vue/dist/vue.esm.js’,所以我們需要修改webpack.dll.config.js配置:
將
vendor: ['vue', 'lodash', 'vuex', 'axios', 'vue-router', 'element-ui']
},
entry: {
vendor: ['vue/dist/vue.esm.js', 'lodash', 'vuex', 'axios', 'vue-router', 'element-ui']
},
由於我們修改了webpack.dll.config.js,所以我們需要重新打包:
## 重新打包dll
npm run dll
## 重新打包原始檔
npm run build
到這裡優化就結束了,但是有個小麻煩,我們每次上線的時候需要新建資料夾,然後把index.html複製貼上進去,再把dist目錄貼上進去,還要把static貼上進去再上線,我們不希望那麼麻煩。
4.提取整個資料夾
首先安裝幾個外掛:
npm install html-webpack-plugin copy-webpack-plugin clean-webpack-plugin --save-dev
然後我們修改webpack.config.js配置:
var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/',
filename: 'build.[hash].js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
}
// other vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
devServer: {
historyApiFallback: true,
noInfo: true,
contentBase: path.join(__dirname, 'dist')
},
performance: {
hints: false
},
devtool: '#eval-source-map',
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./vendor-manifest.json')
}),
new HtmlWebpackPlugin({
inject: true,
template: './index.html',
filename: 'index.html'
}),
new CopyWebpackPlugin([
{ from: 'static', to: 'static' }
]),
new CleanWebpackPlugin(['dist'])
]
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
差別一:
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/',
filename: 'build.[hash].js'
},
- 加了hash值,清除瀏覽器快取
- publicPath: ‘/’: 由於index.html需要打包到dist目錄下,故’build.[hash].js’的引用路徑由/dist/變為/
差別二:
主要是在plugins新添加了三個外掛(千萬不能加在if裡,否則開發環境會出錯):
new HtmlWebpackPlugin({
inject: true,
template: './index.html'
}),
new CopyWebpackPlugin([
{from: 'static', to:'static'}
]),
new CleanWebpackPlugin(['dist'])
- html-webpack-plugin:我們要index.html生成在dist目錄裡,故引用html-webpack-plugin,它預設將生成的index.html打包在output的資料夾下,由於index.html需要引用打包好的靜態dll,且vue需要掛載在根元件div#app上,故需要引入模版,模版為根目錄下index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>dll-test</title>
</head>
<body>
<div id="app"></div>
<script src="./static/js/vendor.dll.js"></script>
</body>
</html>
-
inject:true:將打包好的js檔案注入在該html的body底部,保證了script的載入順序。
-
copy-webpack-plugin:由於我們要引入static/js/中的dll,且是在dist檔案下,故用該外掛將打包好的靜態資料夾拷貝到打包目錄dist下。
-
clean-webpack-plugin:打包前清除dist目錄
差別三:
devServer: {
historyApiFallback: true,
noInfo: true,
contentBase: path.join(__dirname, 'dist')
},
contentBase: path.join(__dirname, ‘dist’),這是為了在dev環境下,提供index.html的目錄改為dist,與生產環境的訪問檔案路徑保持一致。
最後
npm run dev就可以愉快的開發啦,npm run build就可以以極速打包好dist目錄且上線啦。!!!