为什么我们需要dllPlugin
- 不希望在开发热更新时重复打包第三方模块,否则速度太慢。
- 希望在生产环境将第三方模块抽离出去(看起来代码依赖文件体积较大?)
怎么使用?
第三方模块
node_modules/sum/index.js
module.exports = (a, b) => {
return a + b
}
node_modules/sum/package.json
{
main: index.js
}
index.js
import sum from 'sum'
console.log(sum(1, 2))
webpack.dll.config.js
const webpack = require('webpack')
const path = require('path')
// 打包完第三方依赖后,我就要去打包我的业务代码,这个时候就需要让业务代码知道不要再去打包哪些第三方模块了
// 直接从打包好的vendor.js里面去取就可以了。
// 所以,我需要在这里打包第三方依赖的时候,生成一份说明文件manifest.json,来让webpack在打包业务代码的时候
// 知道打包哪些模块需要从vendor里面取而不是重新打包,否则的话,打出来的包还是会很大,会包含那些第三方模块。
module.exports = {
mode: 'development',
entry: {
vendor: ['aaa', 'aaa2']
},
output: {
filename: '[name].js',
library: '[name]',
// libraryTarget: 'umd' // 将入口文件的返回值以什么类型的方式导出,umd比较全,包括amd、cmd、window
},
module: {
rules: [
{
test: /.jsx?$/,
use: 'babel-loader'
}
]
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, './manifest.json')
})
]
}
webpack.dev.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
console.log(require('./config/manifest.json'))
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /.jsx?$/,
use: 'babel-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/template.html'
}),
// webpack在打包业务代码的时候,会通过这个插件参考是否有已经打包在vendor中的模块。
// 如果存在就不会将代码打包进去而是通过一个统一的name(这个name在这里就是下面的name)来引入模块。
// "dll-reference vendor":
// no static exports found */
// (function(module, exports) { // name: vendor
// eval("module.exports = vendor;\n\n//# sourceURL=webpack:///external_%22vendor%22?");
// })
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./config/manifest.json'),
name: 'vendor'
})
]
}
例如以上模块,index.js
在引入sum
模块时,打包后的index.js
文件是
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var sum__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! sum */ "./node_modules/sum/index.js");
/* harmony import */ var sum__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(sum__WEBPACK_IMPORTED_MODULE_0__);
console.log(sum__WEBPACK_IMPORTED_MODULE_0___default()(1, 2));
这时候,模块id为./node_modules/sum/index.js
的内容是
module.exports = (__webpack_require__(/*! dll-reference vendor */ \"dll-reference vendor\"))(\"./node_modules/sum/index.js\")
这时候,模块id为dll-reference vendor
的内容是
module.exports = vendor;
也就是说他会调用全局变量vendor函数,将模块路径(id)传入来获取第三方模块。我们来看下打包出来的vendor包的内容。
// 删除了部分无关代码。
var vendor = (function(modules) {
// webpackBootstrap
// The module cache
var installedModules = {};
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = (installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
});
// Execute the module function
modules[moduleId].call(
module.exports,
module,
module.exports,
__webpack_require__
);
// Flag the module as loaded
module.l = true;
// Return the exports of the module
return module.exports;
}
// Load entry module and return exports
return __webpack_require__((__webpack_require__.s = 0));
})(
/************************************************************************/
{
/***/ "./node_modules/sum/index.js":
/*!***********************************!*\
!*** ./node_modules/sum/index.js ***!
\***********************************/
/*! no static exports found */
/*! all exports used */
/***/ function(module, exports) {
eval(
"module.exports = (a, b) => {\n return a + b;\n};\n\n//# sourceURL=webpack://%5Bname%5D/./node_modules/sum/index.js?"
);
/***/
},
/***/ 0:
/*!******************!*\
!*** dll vendor ***!
\******************/
/*! no static exports found */
/*! all exports used */
/***/ function(module, exports, __webpack_require__) {
eval(
"module.exports = __webpack_require__;\n\n//# sourceURL=webpack://%5Bname%5D/dll_vendor?"
);
/***/
}
}
);
这个打包后的vendor.js
是一个自执行函数,返回的是__webpack_require__
这个函数,并且赋值给了vendor
变量。而第三方模块都会存储在闭包中。当外部调用vendor(id)
的时候,就会执行__webpack_require__(id)
,获取第三方模块。
总结整体流程:
- 通过
dllPlugin
生成manifets.json
和vendor.js
,vendor.js会自执行返回一个加载函数vendor(名字可配置),通过闭包将模块存储在内存中,注意vendor
是一个全局变量。 -
webpack
通过DllReferencePlugin
在打包的时候分析业务代码中使用了哪些第三方模块,哪些模块是不需要打包进业务代码中,而是去vendor.js
中获取。 -
vendor
中获取的模块是通过调用全局函数vendor(id)
来进行引入。