Skip to content

手动分包

基本原理

手动分包的总体思路是:

  1. 先单独的打包公共模块

单独打包公共模块

公共模块会被打包成为动态链接库(dll Dynamic Link Library),并生成资源清单

  1. 根据入口模块进行正常打包

打包时,如果发现模块中使用了资源清单中描述的模块,则不会形成下面的代码结构

js
//源码,入口文件index.js
import $ from "jquery";
import _ from "lodash";
_.isArray($(".red"));

由于资源清单中包含jquerylodash两个模块,因此打包结果的大致格式是:

js
(function (modules) {
  //...
})({
  // index.js文件的打包结果并没有变化
  "./src/index.js": function (module, exports, __webpack_require__) {
    var $ = __webpack_require__("./node_modules/jquery/index.js");
    var _ = __webpack_require__("./node_modules/lodash/index.js");
    _.isArray($(".red"));
  },
  // 由于资源清单中存在,jquery的代码并不会出现在这里
  "./node_modules/jquery/index.js": function (
    module,
    exports,
    __webpack_require__
  ) {
    module.exports = jquery;
  },
  // 由于资源清单中存在,lodash的代码并不会出现在这里
  "./node_modules/lodash/index.js": function (
    module,
    exports,
    __webpack_require__
  ) {
    module.exports = lodash;
  },
});

打包公共模块

打包公共模块是一个独立的打包过程

  1. 单独打包公共模块,暴露变量名
js
// webpack.dll.config.js
module.exports = {
  mode: "production",
  entry: {
    jquery: ["jquery"],
    lodash: ["lodash"],
  },
  output: {
    filename: "dll/[name].js",
    library: "[name]",
  },
};
  1. 利用DllPlugin生成资源清单
js
// webpack.dll.config.js
module.exports = {
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve(__dirname, "dll", "[name].manifest.json"), //资源清单的保存位置
      name: "[name]", //资源清单中,暴露的变量名
    }),
  ],
};

运行后,即可完成公共模块打包

使用公共模块

  1. 在页面中手动引入公共模块
html
<script src="./dll/jquery.js"></script>
<script src="./dll/lodash.js"></script>
  1. 重新设置clean-webpack-plugin

如果使用了插件clean-webpack-plugin,为了避免它把公共模块清除,需要做出以下配置

js
new CleanWebpackPlugin({
  // 要清除的文件或目录
  // 排除掉dll目录本身和它里面的文件
  cleanOnceBeforeBuildPatterns: ["**/*", "!dll", "!dll/*"],
});

目录和文件的匹配规则使用的是globbing patterns

  1. 使用DllReferencePlugin控制打包结果
js
module.exports = {
  plugins: [
    new webpack.DllReferencePlugin({
      manifest: require("./dll/jquery.manifest.json"),
    }),
    new webpack.DllReferencePlugin({
      manifest: require("./dll/lodash.manifest.json"),
    }),
  ],
};

总结

手动打包的过程

  1. 开启output.library暴露公共模块
  2. DllPlugin创建资源清单
  3. DllReferencePlugin使用资源清单

手动打包的注意事项

  1. 资源清单不参与运行,可以不放到打包目录中
  2. 记得手动引入公共 JS,以及避免被删除
  3. 不要对小型的公共 JS 库使用

优点

  1. 极大提升自身模块的打包速度
  2. 极大的缩小了自身文件体积
  3. 有利于浏览器缓存第三方库的公共代码

缺点

  1. 使用非常繁琐
  2. 如果第三方库中包含重复代码,则效果不太理想

最佳实践

手动打包使用场景:

当多个入口 chunk 有相同的依赖,就可以把依赖提取出来,使用分包。

index.js --> jquery lodash

a.js ---> jquery lodash

  • 新建 webpack.dll.config.js 文件,配置打包 dll 的 webpack 文件。
  • 使用内置的webpack.DllPlugin来生成对应的 dll 资源清单文件。
  • 如果使用了clean-webpack-plugin插件,需要配置 dll 文件不被删除。
  • 给需要打包的 webpack.config.js 文件中配置DllReferencePlugin插件,设置打包时的对照规则。
  • 在页面中引入公共模块。
json
{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "dev": "webpack",
    "dll": "webpack --config webpack.dll.config.js"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "clean-webpack-plugin": "^3.0.0",
    "html-webpack-plugin": "^3.2.0",
    "jquery": "^3.4.1",
    "lodash": "^4.17.15",
    "webpack": "^4.41.6",
    "webpack-cli": "^3.3.11"
  }
}
js
const webpack = require("webpack");
const path = require("path");

module.exports = {
  mode: "production",
  entry: {
    jquery: ["jquery"],
    lodash: ["lodash"],
  },
  output: {
    filename: "dll/[name].js",
    library: "[name]", //将name的名字暴露出去
    // libraryTarget: "var", //指定暴露的标准,默认是var
  },

  //webpack内置的DllPlugin,用来生成资源清单,是一个json对象
  plugins: [
    new webpack.DllPlugin({
      //资源清单保存的位置,可以不在dist文件下,因为只是在构建的时候才需要资源清单
      path: path.resolve(__dirname, "dll", "[name].manifest.json"),
      name: "[name]", //生成的资源清单的名称
    }),
  ],
};
js
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");

module.exports = {
  mode: "development",
  devtool: "source-map",
  entry: {
    main: "./src/index.js",
    a: "./src/a.js",
  },
  output: {
    filename: "[name].[hash:5].js",
  },
  //
  plugins: [
    //保证打包的时候dll文件不被清除
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: ["**/*", "!dll", "!dll/*"],
    }),
    new HtmlWebpackPlugin({
      template: "./public/index.html",
    }),
    // 配置dll资源清单比对规则
    new webpack.DllReferencePlugin({
      manifest: require("./dll/jquery.manifest.json"),
    }),
    new webpack.DllReferencePlugin({
      manifest: require("./dll/lodash.manifest.json"),
    }),
  ],
};
js
import _ from "lodash";
import $ from "jquery";

console.log("我是index");

const result = _.isArray($(".red"));
console.log(result);
js
import $ from "jquery";
import _ from "lodash";

const result = _.isArray($(".red"));
console.log(result);
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="./dll/jquery.js"></script>
    <script src="./dll/lodash.js"></script>
  </head>

  <body>
    <div class="red">123</div>
    <div>456</div>
  </body>
</html>

最终生成的文件目录

sh
#根目录下的dll
dll
    ---jquery.manifest.json
    ---lodash.manifest.json
sh
dist
    ---dll
        ---jquery.js
        ---lodash.js
    ---main.as212.js
    ---a.23as65.js
    ---index.html

构建流程梳理

  1. 先利用DllPlugin生成对应的资源清单,将公共模块先打包出来。

  2. 配置打包时,webpack 对照的清单规则,当配置了DllReferencePlugin的规则,webpack 在构建的时候就会先对照资源清单,如果发现分包了,就不会将公共模块再打包进最终的 bundle。

  3. 设置clean-webpack-plugin的清除规则,保证已经打包好的 dll 文件夹下的文件不被清除。

  4. 最终在模板 html 中引用公共模块。

MIT License