引入
在我們的項目中,免不了要引入許多的第三方模塊。這些第三方模塊在打包的時候就會被打包進最後生成的文件之中。導致最後生成的文件過大的同時也增加了打包的時間。
這時我們就會想,如果這些第三方模塊能只打包一次,之後就直接使用這些打包好的模塊多好,畢竟這些第三方的模塊代碼不會有變動。
那麼,我們再次打包目標代碼時就不需要再從node_modules
中去尋找第三方模塊,而是從我們預先打包出的文件中去尋找。
這種想法頗似動態鏈接庫(DLL)。
流程
由於需要單獨打包第三方模塊,也就意味着我們需要一個獨立的Webpack配置文件,我命名爲webpack.dll.js
。內容如下:
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'production',
entry: {
react: ['react', 'react-dom'],
vendors: ['lodash']
},
output: {
path: path.resolve(__dirname, 'dll'),
filename: '[name].dll.js',
library: '[name]'
},
plugins: [
new webpack.DLLPliugin({
name: '[name]',
path: path.resolve(__dirname, 'dll')
})
]
}
這個配置文件的含義是指將react
、react-dom
打包進react.dll.js
這個文件中,而lodash
則打包進vendors.dll.js
裏面。同時分別對外暴露兩個全局變量,分別叫做react
、vendors
。
而plugins
內我使用了一個DLLPlugin
,這個插件會在path
字段所給出的路徑生成一個manifest.json
。這個json
文件包含了import
或require
請求到模塊ID的映射。這個文件在正式打包項目代碼的時候會用到。
接下來就是該正式打包項目代碼了,項目代碼打包使用的Webpack配置文件如下:
const webpack = require('webpack');
const path = require('path');
module.exports = {
...,
plugins: [
new webpack.DLLReferencePlugin({
manifest: path.resolve(__dirname, 'dll/react.manifest.js')
}),
new webpack.DLLReferencePlugin({
manifest: path.resolve(__dirname, 'dll/vendors.manifest.js')
})
]
}
通過引用 dll 的 manifest.json
來把依賴的名稱映射到模塊的 id 上,之後再在需要的時候通過內置的 __webpack_require__
函數來 require 他們。
接下來就可以直接打包了,應該會發現打包速度快了不少。
可是打開頁面會發現,頁面並不能顯示出任何內容,原因是因爲,打包出的dll.js
們並沒有被網頁引入,因此我們還需要一個add-asset-html-webpack-plugin
。
這是一個爲html-webpack-plugin
生成的頁面再添加靜態資源的插件。
以下是完整的plugins
設置。
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new webpack.HotModuleReplacementPlugin(),
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, 'dll/react.dll.js')
}),
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, 'dll/vendors.dll.js')
}),
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, 'dll/react.manifest.json')
}),
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, 'dll/vendors.manifest.json')
})
]
重新打包,這下子就可以在網頁中看到正常現實的內容了。
問題
可能大家也都注意到了,就是項目文件的Webpack配置文件的plugins
寫的非常繁瑣。
假如,打包成DLL的文件又多了幾個。那麼這邊的plugins
是不是還要自己手寫多個new webpack.DLLReferencePlugin
和new AddAssetHtmlWebpackPlugin
?
很顯然,有更智能的寫法。需要藉助Node.js的fs
模塊。
const fs = require('fs');
const plugins = [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new webpack.HotModuleReplacementPlugin(),
]
fs.readdirSync(path.resolve(__dirname, 'dll')).forEach(file => {
const filePath = path.resolve(__dirname, `./dll/${file}`);
if(/.*\.dll.js/.test(file)){
plugins.push(
new AddAssetHtmlWebpackPlugin({
filepath: filePath
})
);
}
if(/.*\.manifest.json/.test(file)){
plugins.push(
new webpack.DllReferencePlugin({
manifest: filePath
})
);
}
});
這段代碼即是檢測dll
目錄下都有哪些manifest.json
和dll.js
文件,並往plugins
中添加相應插件。