引入
在我们的项目中,免不了要引入许多的第三方模块。这些第三方模块在打包的时候就会被打包进最后生成的文件之中。导致最后生成的文件过大的同时也增加了打包的时间。
这时我们就会想,如果这些第三方模块能只打包一次,之后就直接使用这些打包好的模块多好,毕竟这些第三方的模块代码不会有变动。
那么,我们再次打包目标代码时就不需要再从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
中添加相应插件。