序言
其实很多人在面试的时候都会被问到关于webpack优化的问题,不管是构建还是打包加载,可能我们都离不开DllPlugin,也许很多人对DllPlugin已经很熟悉了,但是有很多人对DllPlugin一点都不了解。不管之前有没有过了解,从现在开始我们来分析下DllPlugin插件到底为我们做了什么???
说明
这里其实我们是对dll打包结果以及webpack打包结果进行分析,不对webpack如何打包成dll进行分析,各位郎君切记啊!!!
DllPlugin 是干什么的呢???
例如我们使用vue/ vue-router/ vuex来写项目,虽然我们写代码只有5k,但是打包结果却200k。这是怎么回事呢??? 其实就是将vue等文件都打包到主文件中了。 如果我们想提高加载速度就需要将共同的文件进行抽离,那么插件DllPlugin的机会就来了。
- 具体的看下DllPlugin的定义
DllPlugin 的使用
1. 运行项目结构
同志们!!! 这是我们测试demo的目录结构,后面也会陆陆续续把代码粘贴出来,如果感兴趣的小伙伴也可以自己运行下
1. webpack.dll.config.js 配置实现
const path = require('path')
const { DllPlugin } = require('webpack')
const pathResolve = (url) => path.resolve(__dirname, url)
module.exports = {
mode: 'development',
entry: {
utils: ['isarray', 'is-promise']
},
output: {
path: pathResolve('../dist'),
filename: 'utils.dll.js',
library: '_dll_utils'
},
plugins: [
new DllPlugin({
name: '_dll_utils',
path: path.join(__dirname, '../dist', 'utils.manifest.json')
})
]
}
- 上述的代码就是DllPlugin简单的配置,接下来我们会着重讲解下注意点:
output.library
以及new DllPlugin().name
必须保持一致。因为生成的文件靠这个来进行关联,至于这个是干什么的??? 我们会在后续打包分析中讲解到- 按照上述的配置 我们会生成两个文件
utils.dll.js
以及utils.manifest.json
。 - 插件更多的信息,请参照DllPlugin详细讲解
2. webpack.config.js 配置实现
const path = require('path')
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const pathResolve = (url) => path.resolve(__dirname, url)
module.exports = {
mode: 'development',
devtool: false,
entry: pathResolve('../src/index.js'),
output: {
path: pathResolve('../dist'),
filename: 'bundle.js'
},
plugins: [
new DllReferencePlugin({
manifest: require('../dist/utils.manifest.json')
}),
new HtmlWebpackPlugin({
template: pathResolve('../public/index.html')
})
]
}
- 上述的代码是编译的配置文件,这里还算比较简单。我们的主要关注点在插件
DllReferencePlugin
中。其余的很稀疏平常
3. src/index.js 代码实现
let isarray = require('isarray')
console.log('isarray([1, 2, 3])=', isarray([1, 2, 3]))
4. package.json 配置
{
"scripts": {
"dll": "webpack --config build/webpack.dll.config.js",
"build": "webpack --config build/webpack.config.js"
}
}
编译结果分析
1. 编译结果目录
2. utils.dll.js 代码分析
// 暴露的全局变量 打包内容将以全局的方式挂载到这里
var _dll_utils;
// 最外层的IIFE
(() => {
var __webpack_modules__ = {
"./node_modules/isarray/index.js": (module) => {
eval(
"var toString = {}.toString;\n\nmodule.exports = Array.isArray || function (arr) {\n return toString.call(arr) == '[object Array]';\n};\n\n\n//# sourceURL=webpack://_dll_utils/./node_modules/isarray/index.js?"
);
},
"?2e89": (module, __unused_webpack_exports, __webpack_require__) => {
eval(
"module.exports = __webpack_require__;\n\n//# sourceURL=webpack://_dll_utils/dll_utils?"
);
},
"./node_modules/is-promise/index.mjs": (
__unused_webpack___webpack_module__,
__webpack_exports__,
__webpack_require__
) => {
"use strict";
eval(
"__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ "default": () => (/* binding */ isPromise)\n/* harmony export */ });\nfunction isPromise(obj) {\n return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';\n}\n\n\n//# sourceURL=webpack://_dll_utils/./node_modules/is-promise/index.mjs?"
);
},
};
var __webpack_module_cache__ = {};
// 加载模块方法
function __webpack_require__(moduleId) {
// 获取以及判断缓存
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = (__webpack_module_cache__[moduleId] = {
exports: {},
});
// 开始加载模块
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
var __webpack_exports__ = __webpack_require__("?2e89");
_dll_utils = __webpack_exports__;
})();
- 上述的代码都有对应的注释,请大家可以认真阅读,但是不阅读也没有关系,我们会挨个解析:
- 首先映入眼帘的是代码
var _dll_utils
,这个变量就是我们在webpack.dll.config.js中进行配置的。在代码编译结束后其实是放到全局上的,作为一个全局变量来实现的 - 代码
var __webpack_exports__ = __webpack_require__("?2e89"); _dll_utils = __webpack_exports__;
是执行文件的入口。webpack会给入口一个随机的值,这里就是(?2e89). 开始加载模块 - 上述的描述没有用粘贴代码的方式,而是用截图的方式,主要是怕看不全,不能将前后串链起来。
- 看到代码
module.exports = __webpack_require__;
其实就是将函数(__webpack_require__
)赋值给了_dll_utils全局变量了。 到现在可以这么说了,变量_dll_utils 就是全局加载函数了
- 首先映入眼帘的是代码
3. bandle.js 代码分析
// 最外层IIFE
(() => {
// 模块方法
var __webpack_modules__ = {
// 模块B
"./node_modules/isarray/index.js": (
module,
__unused_webpack_exports,
__webpack_require__
) => {
// 加载模块<dll-reference _dll_utils>
module.exports = __webpack_require__("dll-reference _dll_utils")(
"./node_modules/isarray/index.js"
);
},
"dll-reference _dll_utils": (module) => {
"use strict";
module.exports = _dll_utils;
},
};
var __webpack_module_cache__ = {};
// 加载模块
function __webpack_require__(moduleId) {
// 判断模块中是否存在
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = (__webpack_module_cache__[moduleId] = {
exports: {},
});
// 加载执行模块
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
var __webpack_exports__ = {};
// 执行函数入口
(() => {
// 加载模块<isarray/index.js>
let isarray = __webpack_require__("./node_modules/isarray/index.js");
console.log("isarray([1, 2, 3])=", isarray([1, 2, 3]));
})();
})();
- 上述代码是webpack编译结果,只不过经过了适当的删减,这里还是从入口开始分析:
let isarray = __webpack_require__("./node_modules/isarray/index.js");
左侧代码就是整个文件的执行入口, 开始加载模块- 当我们加载模块
./node_modules/isarray/index.js
的时候,就是执行上述截图中第二个裱框的地方 - 还记得我们上文的关于_dll_utils的分析吗??? 此时_dll_utils其实就是个模块加载函数,而加载的模块上图的
./node_modules/isarray/index.js
这个模块。
- 当我们加载模块
优化:
- 经过这么多的分析,相信大家对这个也有一定的了解。这里我们可以说下关于DllPlugin的优化(打包结束后的*.dll.js):
- 我们可以将文件(*.dll.js)放置到服务器上,但是我们可以使用强缓存的方式来处理。因为一旦我们打包成功了dll文件,意味着文件的变化几率特别小,所以我们可以使用强缓存。但是总归要加载一次呢,第一次怎么办呢??
- 我们可以把文件以cdn的方式进行引入,使用cdn进行加速加载,其实还有很多策略。比如:preload。预加载等, 打包压缩。让包尽可能小 而且 加载快
- 例如我们实际的生产过程中将vue/ vue-router/ vuex/ element-plus/ loadsh 都进行打包。就能在请求时进行加速,速度可想而知啊!!!!!
结尾
好了,废话就这么多了。希望能帮助到大家吧。如果有什么不对的地方,也希望大家多多指正啊。你看说了这么多废话了,自我介绍给忘记了,你看我这脑子。。。