从零到一学会webpack 05-dll

138 阅读3分钟

上一篇:从零到一学会webpack 04-构建同构应用

认识 DLL (动态链接库)

在我们项目中包含大量复用模块的动态链接库只需要编译一次,比如React、react-dom、react-router-dom、polyfill等,除非版本升级,否则这些依赖不会产生更改,dll做的事情,就是再执行一次编辑后,在之后的构建过程中被动态链接库包含的模块将不会在重新编译,而是直接使用动态链接库中的代码

要给 Web 项目构建接入动态链接库的思想,需要完成以下事情:

  • 把网页依赖的基础模块抽离出来,打包到一个个单独的动态链接库中去。一个动态链接库中可以包含多个模块。
  • 当需要导入的模块存在于某个动态链接库中时,这个模块不能被再次被打包,而是去动态链接库中获取。
  • 页面依赖的所有动态链接库需要被加载

Webpack 已经内置了对动态链接库的支持,需要通过2个内置的插件接入,它们分别是:

DllPlugin 插件:用于打包出一个个单独的动态链接库文件。 DllReferencePlugin 插件:用于在主要配置文件中去引入 DllPlugin 插件打包好的动态链接库文件。

构建dll我们需要单独新增一个webpack配置专门处理dll webpack_dll.config.js

const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');

module.exports = {
  // JS 执行入口文件
  entry: {
    // 把 React 相关模块的放到一个单独的动态链接库
    react: ['react', 'react-dom'],
    // 把项目需要的 lodash 放到一个单独的动态链接库
    lodash: ['lodash'],
  },
  output: {
    // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,
    // 也就是 entry 中配置的 react 和 lodash
    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'),
    }),
  ],
};

修改webpack.config.js

const path = require("path");
const webpack = require("webpack");
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');


module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist/"),
    filename: "[name].js"
  },
  mode: "development",
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules|bower_components)/,
        loader: "babel-loader",
        options: { presets: ["@babel/env"] }
      },
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
      }
    ]
  },
  resolve: { extensions: ["*", ".js", ".jsx"] },
  plugins: [
    // new CleanWebpackPlugin(),
    // 告诉 Webpack 使用了哪些动态链接库
    new DllReferencePlugin({
      // 描述 react 动态链接库的文件内容
      manifest: require('./dist/react.manifest.json'),
    }),
    new DllReferencePlugin({
      // 描述 lodash 动态链接库的文件内容
      manifest: require('./dist/lodash.manifest.json'),
    }),
  ],
};

注意:在 webpack_dll.config.js 文件中,DllPlugin 中的 name 参数必须和 output.library 中保持一致。 原因在于 DllPlugin 中的 name 参数会影响输出的 manifest.json 文件中 name 字段的值, 而在 webpack.config.js 文件中 DllReferencePlugin 会去 manifest.json 文件读取 name 字段的值, 把值的内容作为在从全局变量中获取动态链接库中内容时的全局变量名。

到现在我们的文件目录结构为:

├── public
│   └── index.html
├── src
│   ├── App.css
│   ├── App.jsx
│   └── index.js
├── package.json
├── webpack.config.js
├── webpack_dll.config.js
├── .babelrc
└── yarn.lock

我们先执行(==要注意的是需要先把动态链接库相关的文件编译出来,因为主 Webpack 配置文件中定义的 DllReferencePlugin 依赖这些文件==)

webpack --config webpack_dll.config.js

再执行

yarn build

可以看到dist目录成成的文件

├── lodash.dll.js
├── lodash.dll.js.LICENSE.txt
├── lodash.manifest.json
├── main.js
├── react.dll.js
├── react.dll.js.LICENSE.txt
└── react.manifest.json

修改public/index.html文件

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <title>webpack</title>
</head>

<body>
  <div id="root"></div>
  <!--导入依赖的动态链接库文件-->
  <script src="../dist/lodash.dll.js"></script>
  <script src="../dist/react.dll.js"></script>
  <!--导入执行入口文件-->
  <script src="../dist/main.js"></script>
  <noscript>
    You need to enable JavaScript to run this app.
  </noscript>
</body>

</html>

如此我们便完成了dll的生成和引入

备注:package.json的代码为

{
  "name": "webpack-note",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "watch": "webpack --watch",
    "dev": "webpack serve --open",
    "build": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.13.1",
    "@babel/preset-env": "^7.13.5",
    "@babel/preset-react": "^7.12.13",
    "babel-loader": "^8.2.2",
    "css-loader": "^5.0.2",
    "style-loader": "^2.0.0",
    "webpack": "^5.4.0",
    "webpack-cli": "^4.2.0",
    "webpack-dev-server": "^3.11.2",
    "lodash": "^4.17.21",
  },
  "dependencies": {
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-router-dom": "^5.2.0"
  }
}