利用DllPlugin效能優化
介紹
在用 Webpack 打包的時候,對於一些不經常更新的第三方庫,比如 react,lodash,vue 我們希望能和自己的程式碼分離開,Webpack 社群有兩種方案
CommonsChunkPlugin
DLLPlugin
對於 CommonsChunkPlugin,webpack 每次打包實際還是需要去處理這些第三方庫,只是打包完之後,能把第三方庫和我們自己的程式碼分開。而 DLLPlugin 則是能把第三方程式碼完全分離開,即每次只打包專案自身的程式碼。Dll這個概念是借鑑了Windows系統的dll,一個dll包,就是一個純純的依賴庫,它本身不能執行,是用來給你的app引用的
使用
新增配置檔案
要使用 DLLPlugin,需要額外新建一個配置檔案webpack.dll.js,該配置檔案用來打包生成第三方庫。
webpack.dll.js
const webpack = require('webpack') const path = require('path') module.exports = { mode: 'production', entry: { vendor: ['lodash'], react: ['react'] }, output: { filename: '[name].dll.js', path: path.resolve(__dirname, './dll'), library: '[name]', // libraryTarget: 'umd' // 將入口檔案的返回值以什麼型別的方式匯出,umd比較全,包括amd、cmd、window }, plugins: [ new webpack.DllPlugin({ name: '[name]', path: path.join(__dirname, './dll/[name].manifest.json') }) ] }
然後執行 命令列執行"webpack --config webpack-dll.js"
生成打包後的檔案
接下來就是,這麼在業務程式碼裡使用打包好的第三方模組的事情了。
修改配置檔案webpack-common.js
關鍵程式碼如下:
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin'); .... plugins: [ new AddAssetHtmlWebpackPlugin({//將打包好的dll檔案掛載到html中 filepath: path.resolve(__dirname, './dll/react.dll.js') }), new AddAssetHtmlWebpackPlugin({ filepath: path.resolve(__dirname, './dll/vendor.dll.js') }), new webpack.DllReferencePlugin({ manifest: path.resolve(__dirname, './dll/vendor-manifest.json') }), new webpack.DllReferencePlugin({ manifest: path.resolve(__dirname, './dll/react-manifest.json') }) ],
瀏覽器控制檯,可以看到第三方模組,已經作為全域性變數,引入到了入口檔案中。
但是每次在webpack.dll.js新加第三方模組,都要在webpack-common.js裡新增DllReferencePlugin等,比較麻煩,我們可以用node寫一個檔案迴圈,自己讀取檔案數量,自動修改plugin
關鍵程式碼如下:
const fs = require('fs')
const files = fs.readdirSync(path.resolve(__dirname, '../dll'));
const plugins = [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin(),
new BundleAnalyzerPlugin(),
new webpack.ProvidePlugin({
_: 'lodash'
}),
]
files.forEach(file => {
if (/.*\.dll.js/.test(file)) {
plugins.push(new AddAssetHtmlWebpackPlugin({//將打包好的dll檔案掛載到html中
filepath: path.resolve(__dirname, '../dll', file)
}))
}
if (/.*\.manifest.json/.test(file)) {
plugins.push(new webpack.DllReferencePlugin({//分析第三方模組是否已經在dll檔案裡,如果裡面有就不用再node_modules在分析打包了
manifest: path.resolve(__dirname, '../dll', file)
}))
}
})
總結整體流程
通過dllPlugin生成manifets.json和vendor.js,vendor.js會自執行返回一個載入函式vendor(名字可配置),通過閉包將模組儲存在記憶體中,注意vendor是一個全域性變數。
webpack通過DllReferencePlugin在打包的時候分析業務程式碼中使用了哪些第三方模組,哪些模組是不需要打包進業務程式碼中,而是去vendor.js中獲取。
vendor中獲取的模組是通過呼叫全域性函式vendor(id)來進行引入。
補充
CommonsChunkPlugin 外掛每次打包的時候還是會去處理一些第三方依賴庫,只是它能把第三方庫檔案和我們的程式碼分開掉,生成一個獨立的js檔案。但是它還是不能提高打包的速度。
DLLPlugin 它能把第三方庫程式碼分離開,並且每次檔案更改的時候,它只會打包該專案自身的程式碼。所以打包速度會更快。
DLLPlugin 這個外掛是在一個額外獨立的webpack設定中建立一個只有dll的bundle,也就是說我們在專案根目錄下除了有webpack.config.js,還會新建一個webpack.dll.config.js檔案。webpack.dll.config.js作用是把所有的第三方庫依賴打包到一個bundle的dll檔案裡面,還會生成一個名為 manifest.json檔案。
該manifest.json的作用是用來讓 DllReferencePlugin 對映到相關的依賴上去的。
DllReferencePlugin 這個外掛是在webpack.config.js中使用的,該外掛的作用是把剛剛在webpack.dll.config.js中打包生成的dll檔案引用到需要的預編譯的依賴上來。什麼意思呢?就是說在webpack.dll.config.js中打包後比如會生成 vendor.dll.js檔案和vendor-manifest.json檔案,vendor.dll.js檔案包含所有的第三方庫檔案,vendor-manifest.json檔案會包含所有庫程式碼的一個索引,當在使用webpack.config.js檔案打包DllReferencePlugin外掛的時候,會使用該DllReferencePlugin外掛讀取vendor-manifest.json檔案,看看是否有該第三方庫。vendor-manifest.json檔案就是有一個第三方庫的一個對映而已。
所以說 第一次使用 webpack.dll.config.js 檔案會對第三方庫打包,打包完成後就不會再打包它了,然後每次執行 webpack.config.js檔案的時候,都會打包專案中本身的檔案程式碼,當需要使用第三方依賴的時候,會使用 DllReferencePlugin外掛去讀取第三方依賴庫。所以說它的打包速度會得到一個很大的提升。