在介绍 DllPlugin 前先给大家介绍下 DLL。 用过 Windows 系统的人应该会经常看到以 .dll 为后缀的文件,这些文件称为动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据。
.dll
要给 Web 项目构建接入动态链接库的思想,需要完成以下事情:
为什么给 Web 项目构建接入动态链接库的思想后,会大大提升构建速度呢? 原因在于包含大量复用模块的动态链接库只需要编译一次,在之后的构建过程中被动态链接库包含的模块将不会在重新编译,而是直接使用动态链接库中的代码。 由于动态链接库中大多数包含的是常用的第三方模块,例如 react、react-dom,只要不升级这些模块的版本,动态链接库就不用重新编译。
Webpack 已经内置了对动态链接库的支持,需要通过2个内置的插件接入,它们分别是:
下面以基本的 React 项目为例,为其接入 DllPlugin,在开始前先来看下最终构建出的目录结构:
├── main.js ├── polyfill.dll.js ├── polyfill.manifest.json ├── react.dll.js └── react.manifest.json
其中包含两个动态链接库文件,分别是:
polyfill.dll.js
react.dll.js
以 react.dll.js 文件为例,其文件内容大致如下:
var _dll_react = (function(modules) { // ... 此处省略 webpackBootstrap 函数代码 }([ function(module, exports, __webpack_require__) { // 模块 ID 为 0 的模块对应的代码 }, function(module, exports, __webpack_require__) { // 模块 ID 为 1 的模块对应的代码 }, // ... 此处省略剩下的模块对应的代码 ]));
可见一个动态链接库文件中包含了大量模块的代码,这些模块存放在一个数组里,用数组的索引号作为 ID。 并且还通过 _dll_react 变量把自己暴露在了全局中,也就是可以通过 window._dll_react 可以访问到它里面包含的模块。
_dll_react
window._dll_react
其中 polyfill.manifest.json 和 react.manifest.json 文件也是由 DllPlugin 生成出,用于描述动态链接库文件中包含哪些模块, 以 react.manifest.json 文件为例,其文件内容大致如下:
polyfill.manifest.json
react.manifest.json
{ // 描述该动态链接库文件暴露在全局的变量名称 "name": "_dll_react", "content": { "./node_modules/process/browser.js": { "id": 0, "meta": {} }, // ... 此处省略部分模块 "./node_modules/react-dom/lib/ReactBrowserEventEmitter.js": { "id": 42, "meta": {} }, "./node_modules/react/lib/lowPriorityWarning.js": { "id": 47, "meta": {} }, // ... 此处省略部分模块 "./node_modules/react-dom/lib/SyntheticTouchEvent.js": { "id": 210, "meta": {} }, "./node_modules/react-dom/lib/SyntheticTransitionEvent.js": { "id": 211, "meta": {} }, } }
可见 manifest.json 文件清楚地描述了与其对应的 dll.js 文件中包含了哪些模块,以及每个模块的路径和 ID。
manifest.json
dll.js
main.js 文件是编译出来的执行入口文件,当遇到其依赖的模块在 dll.js 文件中时,会直接通过 dll.js 文件暴露出的全局变量去获取打包在 dll.js 文件的模块。 所以在 index.html 文件中需要把依赖的两个 dll.js 文件给加载进去,index.html 内容如下:
main.js
index.html
<html> <head> <meta charset="UTF-8"> </head> <body> <div id="app"></div> <!--导入依赖的动态链接库文件--> <script src="./dist/polyfill.dll.js"></script> <script src="./dist/react.dll.js"></script> <!--导入执行入口文件--> <script src="./dist/main.js"></script> </body> </html>
以上就是所有接入 DllPlugin 后最终编译出来的代码,接下来教你如何实现。
构建输出的以下这四个文件
├── polyfill.dll.js ├── polyfill.manifest.json ├── react.dll.js └── react.manifest.json
和以下这一个文件
├── main.js
是由两份不同的构建分别输出的。
动态链接库文件相关的文件需要由一份独立的构建输出,用于给主构建使用。新建一个 Webpack 配置文件 webpack_dll.config.js 专门用于构建它们,文件内容如下:
webpack_dll.config.js
const path = require('path'); const DllPlugin = require('webpack/lib/DllPlugin'); module.exports = { // JS 执行入口文件 entry: { // 把 React 相关的放到一个单独的动态链接库 react: ['react', 'react-dom'], // 把项目需要所有的 polyfill 放到一个单独的动态链接库 polyfill: ['core-js/fn/object/assign', 'core-js/fn/promise', 'whatwg-fetch'], }, output: { // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称, // 也就是 entry 中配置的 react 和 polyfill filename: '[name].dll.js', // 输出的文件都放到 dist 目录下 path: path.resolve(__dirname, 'dist'), // 存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react // 之所以在前面加上 _dll_ 是为了防止全局变量冲突 library: '_dll_[name]', }, plugins: [ // 接入 DllPlugin new DllPlugin({ // 动态链接库的全局变量名称,需要和 output.library 中保持一致 // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值 // 例如 react.manifest.json 中就有 "name": "_dll_react" name: '_dll_[name]', // 描述动态链接库的 manifest.json 文件输出时的文件名称 path: path.join(__dirname, 'dist', '[name].manifest.json'), }), ], };
构建出的动态链接库文件用于给其它地方使用,在这里也就是给执行入口使用。
用于输出 main.js 的主 Webpack 配置文件内容如下:
const path = require('path'); const DllReferencePlugin = require('webpack/lib/DllReferencePlugin'); module.exports = { entry: { // 定义入口 Chunk main: './main.js' }, output: { // 输出文件的名称 filename: '[name].js', // 输出文件都放到 dist 目录下 path: path.resolve(__dirname, 'dist'), }, module: { rules: [ { // 项目源码使用了 ES6 和 JSX 语法,需要使用 babel-loader 转换 test: /\.js$/, use: ['babel-loader'], exclude: path.resolve(__dirname, 'node_modules'), }, ] }, plugins: [ // 告诉 Webpack 使用了哪些动态链接库 new DllReferencePlugin({ // 描述 react 动态链接库的文件内容 manifest: require('./dist/react.manifest.json'), }), new DllReferencePlugin({ // 描述 polyfill 动态链接库的文件内容 manifest: require('./dist/polyfill.manifest.json'), }), ], devtool: 'source-map' };
注意:在 webpack_dll.config.js 文件中,DllPlugin 中的 name 参数必须和 output.library 中保持一致。 原因在于 DllPlugin 中的 name 参数会影响输出的 manifest.json 文件中 name 字段的值, 而在 webpack.config.js 文件中 DllReferencePlugin 会去 manifest.json 文件读取 name 字段的值, 把值的内容作为在从全局变量中获取动态链接库中内容时的全局变量名。
webpack.config.js
在修改好以上两个 Webpack 配置文件后,需要重新执行构建。 重新执行构建时需要注意的是需要先把动态链接库相关的文件编译出来,因为主 Webpack 配置文件中定义的 DllReferencePlugin 依赖这些文件。
执行构建时流程如下:
webpack --config webpack_dll.config.js
webpack
本实例提供项目完整代码
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8