Skip to content

DllPlugin 资源提取配置

  • 该功能使用时实际上就是提前生成一个需要抽取出去的映射 json 文件,然后在使用时读取该映射文件,在映射文件中存在记录的便不在打包构建。该映射文件包括了全局挂载的变量函数名称,以及使用该函数需要入参键值等;
  • 有了 splitChunks 情况下,为何还需要该插件。splitChunks 一般只用来做代码分割,并且每次都会重新构建,也没有以独立配置的形式确定分包的代码,多页的共用性有限。DllPlugin 可以指定将某些包单独构建出来,尤其是在多页时可极大的提高共用。将常用的工具类和基础框架单独构建,也可以提升每次打包的性能,无需重复打包构建;
  • 随着webpack4 之后的优化, 尤其是webpack5 加入缓存之后, 单独为了提升打包速度的这种拆分意义已经不大, 使用场景比较有限;
  • 实际开发经验中使用较多的场景是多个子项目独立部署且互不干扰的场景,每个子项目都生成自己单独的文件目录, 但是又共用了一套基础框架体系,这个时候可以抽取一部分公共代码,放在单独的文件夹中当作公用,每个子项目根据路径引入便可,常见的比如一个个活动页面,一个个独立产品的页面等...,实际很多项目中可能都不需要使用该配置;

配置使用介绍, 主要分三步

在实际使用过程中一些公共的变量一般都会抽取出来单独定义,提供多处使用,尤其是dll配置中用到的名称和路径配置等共有变量;

  1. 生成映射文件和需要抽取库的文件。创建 单独的 webpack.dll.config.js 文件, 包含webpack的基本配置, 使用webpack命令运行或者使用webpack编译该文件, 示例如下:
javascript
/** 
 * 因为使用webpack命令还需要单独下载 webpack-cli, 此处直接使用webpack编译的方式, 也方便进行参数传递
 * 也可以利用 webpack 的命令直接指定配置文件, 此时 module.exports 导出的应该是直接的对象
 */
const path = require('path');
const webpack = require('webpack');

module.exports = function (webpackEnv) {
  // const isDev = webpackEnv === 'development';
  // const isPrd = webpackEnv === 'production';

  /** 生成的文件名称, 关联环境和版本版本, 可自行制定生成规则 */
  const dllFileName = 'react.xxxx~react-dom.xxxx';
  const dllPath = path.join(__dirname, 'public/dll');
  return {
    mode: webpackEnv,
    entry: {
      /** 需要抽取的公共包 */
      vendor: [
        'react',
        'react-dom',
      ],
    },
    output: {
      /** 抽取的 js 公共文件名称 */
      filename: `${dllFileName}.js`,
      /** 抽取的 js 文件存放目录 */
      path: dllPath,
      /** 生成的全局变量函数名称 */
      library: 'dll_lib',
    },
    plugins: [
      new webpack.DllPlugin({
        /** 生成的全局变量函数名称, 与上面需要对应 */
        name: 'dll_lib',
        /** 生成的 json 映射文件名称 */
        path: path.join(dllPath, `${dllFileName}.json`),
      }),
    ],
  };
};
// 使用时创建 dll.js, 需要构建时加一行命令运行该文件便可, 当然实际 run 时应该根据各种场景抛出异常, 此处简要处理
const webpack = require('webpack');
const dllConfigFun = require('./webpack.dll.config');
const compiler = webpack(dllConfigFun('production'));
compiler.run((err, stats) => {});

// 一般情况可创建命令,实际根据情况来处理,如:
"dll": "node dll.js"

// npm run dll
  1. 在项目中,在入口页面引入生成的公共js文件。

因为打包项目已经绕过了该文件包含的一些库的处理,利用 HtmlWebpackPlugin 插件功能提供变量配置,然后在 html 模版文件中即可使用该变量引入文件。当然实际项目中可能存在别的配置方式, 比如 create-react-app 就是使用自定义的插件来注入的, 在配置文件中配置便可,此处不做详细介绍。

javascript
const path = require('path');

const dllFileName = 'react.xxxx~react-dom.xxxx';

plugins: {
  ...,
  new HtmlWebpackPlugin({
    ...,
    // 定义生成的抽取文件的访问路径, 可根据项目实际访问目录来设置
    DLL_PATH: `/dll/${dllFileName}.js`,
    ...,
  }),
  ...,
},

// 在项目入口文件引入, 很多是 index.html,根据实际项目入口来增加引入
<script src="<%= htmlWebpackPlugin.options.DLL_PATH %>"></script>
  1. 在项目中 使用 webpack.DllReferencePlugin 插件指定映射的json文件,让项目在打包过程中绕过这些文件,从而达到抽取公共库的目的。
javascript
// 再项目的基础配置中增加插件配置
const path = require('path');
const webpack = require('webpack');

const dllFileName = 'react.xxxx~react-dom.xxxx';
const dllPath = path.join(__dirname, 'public/dll');

plugins: {
  ...,
  /** 使用 dll 拆分文件, 实际上就是读取映射关系, 在 json 映射关系中的就不会再次构建打包了 */
  new webpack.DllReferencePlugin({
    /** 此处必须是项目根目录, 也就是 package.json 所在目录 */
    context: path.join(__dirname, '.'),
    /** 映射文件路径, 一般都是全路径 */
    manifest: path.join(dllPath, `${dllFileName}.json`),
  }),
  ...,
}

经过如上3步,dll的配置便完成。

生成的映射 json 文件内容简要介绍,以本示例为例,抽取的 react, react-dom, production 模式下生成的映射文件如下:
javascript
{
  "name": "dll_lib",
  "content": {
    "./node_modules/react/index.js": {
      "id": 294,
      "buildMeta": {
        "exportsType": "dynamic",
        "defaultObject": "redirect"
      },
      "exports": [
        "Children",
        "Component",
        "Fragment",
        "Profiler",
        "PureComponent",
        "StrictMode",
        "Suspense",
        "__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED",
        "cloneElement",
        "createContext",
        "createElement",
        "createFactory",
        "createRef",
        "forwardRef",
        "isValidElement",
        "lazy",
        "memo",
        "startTransition",
        "unstable_act",
        "useCallback",
        "useContext",
        "useDebugValue",
        "useDeferredValue",
        "useEffect",
        "useId",
        "useImperativeHandle",
        "useInsertionEffect",
        "useLayoutEffect",
        "useMemo",
        "useReducer",
        "useRef",
        "useState",
        "useSyncExternalStore",
        "useTransition",
        "version"
      ]
    },
    "./node_modules/react-dom/index.js": {
      "id": 935,
      "buildMeta": {
        "exportsType": "dynamic",
        "defaultObject": "redirect"
      },
      "exports": [
        "__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED",
        "createPortal",
        "createRoot",
        "findDOMNode",
        "flushSync",
        "hydrate",
        "hydrateRoot",
        "render",
        "unmountComponentAtNode",
        "unstable_batchedUpdates",
        "unstable_renderSubtreeIntoContainer",
        "version"
      ]
    }
  }
}

// 使用的时候大概如下所示
dll_lib('./node_modules/react/index.js'); 便能够得到所有 exports 的方法。