DllPlugin

什么是DLL

  • DllPluginDllReferencePlugin提供了拆分包的方法,可以极大地提高构建时性能。术语DLL代表动态链接库,它最初是由Microsoft引入的。
  • .dll为后缀的文件称为动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据
  • 把基础模块独立出来打包到单独的动态连接库里
  • 当需要导入的模块在动态连接库里的时候,模块不能再次被打包,而是去动态连接库里获取

使用DLL

安装依赖

js 复制代码
cnpm i webpack webpack-cli html-webpack-plugin isarray is-promise -D

定义Dll

  • dll-plugin
  • DllPlugin插件: 用于打包出一个个动态连接库
  • DllReferencePlugin: 在配置文件中引入DllPlugin插件打包好的动态连接库

webpack.dll.config.js

js 复制代码
const path = require("path");
const DllPlugin = require("webpack/lib/DllPlugin");
const DllPlugin2 = require("./plugins/DllPlugin");
module.exports = {
  mode: "development",
  devtool: false,
  entry: {
    utils:["isarray","is-promise"]
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "utils.dll.js", //react.dll.js
    library: "_dll_utils",
  },
  plugins: [
    new DllPlugin2({
      //暴露出去的dll函数
      name: "_dll_utils",
      //输出的manifest json文件的绝对路径
      path: path.join(__dirname, "dist", "utils.manifest.json")
    }),
  ],
};

使用动态链接库文件

webpack.config.js

js 复制代码
const path = require("path");
const DllReferencePlugin = require("webpack/lib/DllReferencePlugin.js");
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  mode: "development",
  devtool: false,
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  plugins: [
    new DllReferencePlugin({
      manifest: require("./dist/utils.manifest.json"),
    }),
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
};

html中使用

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>dll</title>
</head>
<body>
    <div id="root"></div>
    <script src="utils.dll.js"></script>
</body>
</html>

index.js

src\index.js

js 复制代码
let isarray = require("isarray");
console.log('isarray([1, 2, 3])=',isarray([1, 2, 3]));

package.json

package.json

json 复制代码
  "scripts": {
    "dll": "webpack --config webpack.dll.config.js",
    "build": "webpack --config webpack.config.js"
  }

dll.js

js 复制代码
const webpack = require("webpack");
const webpackOptions = require("./webpack.dll.config");
const compiler = webpack(webpackOptions);
debugger
compiler.run((err, stats) => {
  console.log(
    stats.toJson({})
  );
});

build.js

js 复制代码
const webpack = require("webpack");
const webpackOptions = require("./webpack.config");
const compiler = webpack(webpackOptions);
compiler.run((err, stats) => {
  console.log(
    stats.toJson({})
  );
});

打包文件分析

index.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>dll</title>
</head>
<body>
    <div id="root"></div>
    <script src="utils.dll.js"></script>
    <script src="bundle.js"></script></body>
</html>

utils.dll.js

dist\utils.dll.js

js 复制代码
var _dll_utils =
  (function (modules) {
    var installedModules = {};
    function __webpack_require__(moduleId) {
      if (installedModules[moduleId]) {
        return installedModules[moduleId].exports;
      }
      var module = installedModules[moduleId] = {
        i: moduleId,
        l: false,
        exports: {}
      };
      modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
      module.l = true;
      return module.exports;
    }
    return __webpack_require__(__webpack_require__.s = 0);
  })
    ({
      "./node_modules/_is-promise@4.0.0@is-promise/index.js":
        (function (module, exports) {
          module.exports = isPromise;
          module.exports.default = isPromise;
          function isPromise(obj) {
            return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
          }
        }),
      "./node_modules/_isarray@2.0.5@isarray/index.js":
        (function (module, exports) {
          var toString = {}.toString;
          module.exports = Array.isArray || function (arr) {
            return toString.call(arr) == '[object Array]';
          };
        }),
      0:
        (function (module, exports, __webpack_require__) {
          module.exports = __webpack_require__;
        })
    });

utils.manifest.json

dist\utils.manifest.json

json 复制代码
{
  "name": "_dll_utils",
  "content": {
    "./node_modules/_is-promise@4.0.0@is-promise/index.js": {
      "id": "./node_modules/_is-promise@4.0.0@is-promise/index.js",
      "buildMeta": { "providedExports": true }
    },
    "./node_modules/_isarray@2.0.5@isarray/index.js": {
      "id": "./node_modules/_isarray@2.0.5@isarray/index.js",
      "buildMeta": {
        "providedExports": true
      }
    }
  }
}

bundle.js

dist\bundle.js

js 复制代码
(function (modules) {
  var installedModules = {};
  function __webpack_require__(moduleId) {
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    };
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    module.l = true;
    return module.exports;
  }
  return __webpack_require__(__webpack_require__.s = "./src/index.js");
})
  ({
    "./node_modules/_isarray@2.0.5@isarray/index.js":
      (function (module, exports, __webpack_require__) {
        module.exports = (__webpack_require__("dll-reference _dll_utils"))("./node_modules/_isarray@2.0.5@isarray/index.js");
      }),
    "./src/index.js":
      (function (module, exports, __webpack_require__) {
        let isarray = __webpack_require__("./node_modules/_isarray@2.0.5@isarray/index.js");
        console.log('isarray([1, 2, 3])=', isarray([1, 2, 3]));
      }),
    "dll-reference _dll_utils":
      (function (module, exports) {
        module.exports = _dll_utils;
      })
  });

实现DllPlugin.js

DllPlugin.js

plugins\DllPlugin.js

js 复制代码
const DllEntryPlugin = require("./DllEntryPlugin");
class DllPlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {
        compiler.hooks.entryOption.tap("DllPlugin", (context, entry) => {
            Object.keys(entry).forEach(name => {
                new DllEntryPlugin(context, entry[name],name).apply(compiler);
            });
            return true;
        });
    }
}
module.exports = DllPlugin;

DllEntryPlugin.js

plugins\DllEntryPlugin.js

js 复制代码
const SingleEntryDependency = require("webpack/lib/dependencies/SingleEntryDependency");
const DllEntryDependency = require("./dependencies/DllEntryDependency");
const DllModuleFactory = require("./DllModuleFactory");
class DllEntryPlugin {
    constructor(context, entries, name) {
        this.context = context;// c:/aprepare/zhufeng_dll_prepare
        this.entries = entries;// ['isarray', 'is-promise']
        this.name = name; // utils
    }
    apply(compiler) {
        compiler.hooks.compilation.tap(
            "DllEntryPlugin",
            (compilation, { normalModuleFactory }) => {
                const dllModuleFactory = new DllModuleFactory();
                compilation.dependencyFactories.set(
                    DllEntryDependency,
                    dllModuleFactory
                );
                compilation.dependencyFactories.set(
                    SingleEntryDependency,
                    normalModuleFactory
                );
            }
        );
        compiler.hooks.make.tapAsync("DllEntryPlugin", (compilation, callback) => {
            compilation.addEntry(
                this.context,
                new DllEntryDependency(
                    this.entries.map((entry) => new SingleEntryDependency(entry)),
                    this.name//utils
                ),
                this.name,//utils
                callback
            );
        });
    }
}
module.exports = DllEntryPlugin;

DllModuleFactory.js

plugins\DllModuleFactory.js

js 复制代码
const { Tapable } = require("tapable");
const DllModule = require("./DllModule");
class DllModuleFactory extends Tapable {
    constructor() {
        super();
        this.hooks = {};
    }
    create(data, callback) {
        const dependency = data.dependencies[0];
        callback(
            null,
            new DllModule(
                data.context,
                dependency.dependencies,// [SingleEntryDependency, SingleEntryDependency]
                dependency.name,//utils
                dependency.type//'dll entry'
            )
        );
    }
}
module.exports = DllModuleFactory;

DllEntryDependency.js

plugins\dependencies\DllEntryDependency.js

js 复制代码
const Dependency = require("webpack/lib/Dependency");
class DllEntryDependency extends Dependency {
    constructor(dependencies, name) {
        super();
        this.dependencies = dependencies;//[SingleEntryDependency,SingleEntryDependency ]
        this.name = name;//utils
    }
    get type() {
        return "dll entry";
    }
}
module.exports = DllEntryDependency;

DllModule.js

plugins\DllModule.js

js 复制代码
const { RawSource } = require("webpack-sources");
const Module = require("webpack/lib/Module");
class DllModule extends Module {
    constructor(context, dependencies, name, type) {
        super("javascript/dynamic", context);//c:/aprepare/zhufeng_dll_prepare2
        this.dependencies = dependencies;
        this.name = name;//utils
        this.type = type;//dll entry
    }
    identifier() {
        return `dll ${this.name}`;
    }
    readableIdentifier() {
        return `dll ${this.name}`;
    }
    build(options, compilation, resolver, fs, callback) {
        this.built = true;
        this.buildMeta = {};
        this.buildInfo = {};
        return callback();
    }
    source() {
        return new RawSource("module.exports = __webpack_require__;");
    }
    size() {
        return 12;
    }
}
module.exports = DllModule;

实现LibManifestPlugin.js

LibManifestPlugin.js

plugins\LibManifestPlugin.js

js 复制代码
const path = require("path");
const asyncLib = require("neo-async");
class LibManifestPlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {
        compiler.hooks.emit.tapAsync(
            "LibManifestPlugin",
            (compilation, callback) => {
                asyncLib.forEach(
                    compilation.chunks,
                    (chunk, done) => {
                        // c:aprepare/zhufeng_dll_prepare/dist/utils.manifest.json
                        const targetPath = this.options.path;
                        const name =this.options.name;//_dll_utils
                        let content ={};
                        for(let module of chunk.modulesIterable){
                            if (module.libIdent) {
                                const ident = module.libIdent({context:compiler.options.context});
                                content[ident]= {id: module.id};
                            }
                        }
                        const manifest = {name,content};//name=_dll_utils
                        compiler.outputFileSystem.mkdirp(path.dirname(targetPath), err => {
                            compiler.outputFileSystem.writeFile(
                                targetPath,
                                JSON.stringify(manifest),
                                done
                            );
                        });
                    },
                    callback
                );
            }
        );
    }
}
module.exports = LibManifestPlugin;

DllPlugin.js

plugins\DllPlugin.js

diff 复制代码
const DllEntryPlugin = require("./DllEntryPlugin");
+const LibManifestPlugin = require("./LibManifestPlugin");
class DllPlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {
        compiler.hooks.entryOption.tap("DllPlugin", (context, entry) => {
            Object.keys(entry).forEach(name => {
                new DllEntryPlugin(context, entry[name],name).apply(compiler);
            });
            return true;
        });
+        new LibManifestPlugin(this.options).apply(compiler);
    }
}
module.exports = DllPlugin;

实现DllReferencePlugin.js

DllReferencePlugin.js

plugins\DllReferencePlugin.js

js 复制代码
const DelegatedSourceDependency = require("webpack/lib/dependencies/DelegatedSourceDependency");
const ExternalModuleFactoryPlugin = require("./ExternalModuleFactoryPlugin");
const DelegatedModuleFactoryPlugin = require("./DelegatedModuleFactoryPlugin");
class DllReferencePlugin {
    constructor(options) {
        this.options = options;
    }

    apply(compiler) {
        compiler.hooks.compilation.tap(
            "DllReferencePlugin",
            (compilation, { normalModuleFactory }) => {
                compilation.dependencyFactories.set(
                    DelegatedSourceDependency,
                    normalModuleFactory
                );
            }
        );

        compiler.hooks.compile.tap("DllReferencePlugin", ({normalModuleFactory}) => {
            let manifest = this.options.manifest;
            let name = manifest.name;//_dll_utils
            let content = manifest.content;//{'is-promise':'','isarray':''}
            //外部模块  "dll-reference _dll_utils"引用window._dll_utils
            const externals = {};
            const source = "dll-reference " + name;//dll-reference _dll_utils
            externals[source] = name;//dll-reference _dll_utils=>_dll_utils
      new ExternalModuleFactoryPlugin("var", externals).apply(normalModuleFactory);

            new DelegatedModuleFactoryPlugin({
                source,
                context: compiler.options.context,
                content
            }).apply(normalModuleFactory);
        });
    }
}

module.exports = DllReferencePlugin;

ExternalModuleFactoryPlugin.js

plugins\ExternalModuleFactoryPlugin.js

js 复制代码
const ExternalModule = require('webpack/lib/ExternalModule');
class ExternalModuleFactoryPlugin{
    constructor(type, externals) {
        this.type = type;//var
        this.externals = externals;//{"dll-reference _dll_utils":"_dll_utils"}
    }

    apply(normalModuleFactory) {
        //const globalType = this.type;
        normalModuleFactory.hooks.factory.tap(
            "ExternalModuleFactoryPlugin",
            //传进去老的工厂函数,返回新的工厂函数
            factory => (data, callback) => {
         const dependency = data.dependencies[0];//DelegatedSourceDependency
         let request = dependency.request;// "dll-reference _dll_utils"
         let value = this.externals[request];//_dll_utils
         if(value){//如果是一个外部模块
             callback(
                 null,
                 new ExternalModule(value, 'var', dependency.request)//_dll_utils
             );
         }else{//否则 是个普通模块 走老的普通模块工厂的生产模块的逻辑
             factory(data, callback);
         }
            }
        );
    }
}
module.exports = ExternalModuleFactoryPlugin;

DelegatedModuleFactoryPlugin.js

plugins\DelegatedModuleFactoryPlugin.js

js 复制代码
const DelegatedModule = require("./DelegatedModule");
class DelegatedModuleFactoryPlugin {
    constructor(options) {
        this.options = options;
        options.type = options.type || "require";
    }
    apply(normalModuleFactory) {
        normalModuleFactory.hooks.module.tap(
            "DelegatedModuleFactoryPlugin",
            module => {
                if (module.libIdent) {
                    const request = module.libIdent(this.options);
                    if (request && request in this.options.content) {
                        const resolved = this.options.content[request];
                        return new DelegatedModule(
                            this.options.source,//dll-reference _dll_utils
                            resolved,//{"id":"./node_modules/_is-promise@4.0.0@is-promise/index.js"}
                            module//老模块
                        );
                    }
                }
                return module;
            }
        );
    }
}
module.exports = DelegatedModuleFactoryPlugin;

DelegatedModule.js

plugins\DelegatedModule.js

js 复制代码
const { RawSource } = require("webpack-sources");
const DelegatedSourceDependency = require("webpack/lib/dependencies/DelegatedSourceDependency");
const Module = require("webpack/lib/Module");
class DelegatedModule extends Module {
    constructor(sourceRequest, data,originalRequest) {
        super("javascript/dynamic", null);
        this.sourceRequest = sourceRequest;//dll-reference _dll_utils
        this.request = data.id;//{"id":"./node_modules/_is-promise@4.0.0@is-promise/index.js"}
        this.originalRequest = originalRequest;//老模块
    }
    libIdent(options) {//模块ID还是老的模块ID
        return this.originalRequest.libIdent(options);
    }
    identifier() {
        return `delegated ${this.request} from ${this.sourceRequest}`;
    }

    readableIdentifier() {
        return `delegated ${this.request} from ${this.sourceRequest}`;
    }
    size(){
        return 42;
    }
    build(options, compilation, resolver, fs, callback) {
        this.built = true;
        this.buildMeta = {};
        this.buildInfo = {};
        this.delegatedSourceDependency = new DelegatedSourceDependency(
            this.sourceRequest
        );
        this.addDependency(this.delegatedSourceDependency);
        callback();
    }
    source() {
        let str = `module.exports = (__webpack_require__("${this.sourceRequest}"))(${JSON.stringify(this.request)});`;
        return new RawSource(str);
    }
}
module.exports = DelegatedModule;

autodll-webpack-plugin

webpack.config.js

js 复制代码
const path = require('path');
const AutodllWebpackPlugin = require('autodll-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    mode:'development',
    devtool:false,
    entry:'./src/index.js',
    output:{
        path:path.resolve(__dirname,'dist'),//输出目录
        filename:'bundle.js',               //打包后的文件名
        publicPath:''                       //访问路径 
    },
    plugins:[
        new HtmlWebpackPlugin({
            inject: true,
            template: './src/index.html',
        }),
        new AutodllWebpackPlugin({
            inject: true,
            filename: '[name].dll.js',
            entry:{
                utils:['isarray','is-promise']
            }
        })
    ]
}

plugin.js

node_modules_autodll-webpack-plugin@0.4.2@autodll-webpack-plugin\lib\plugin.js

diff 复制代码
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+        compiler.hooks.compilation.tap('AutoDllPlugin', function (compilation) {
+          if (compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration) {
+            compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration.tapAsync('AutoDllPlugin', doCompilation);
+          } else if (HtmlWebpackPlugin.getHooks && HtmlWebpackPlugin.getHooks(compilation)) {
+            HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync('AutoDllPlugin', doCompilation);
+          }
+        });
相关推荐
大怪v1 小时前
【Virtual World 04】我们的目标,无限宇宙!!
前端·javascript·代码规范
狂炫冰美式1 小时前
不谈技术,搞点文化 🧀 —— 从复活一句明代残诗破局产品迭代
前端·人工智能·后端
xw52 小时前
npm几个实用命令
前端·npm
!win !2 小时前
npm几个实用命令
前端·npm
代码狂想家2 小时前
使用openEuler从零构建用户管理系统Web应用平台
前端
dorisrv4 小时前
优雅的React表单状态管理
前端
蓝瑟4 小时前
告别重复造轮子!业务组件多场景复用实战指南
前端·javascript·设计模式
dorisrv4 小时前
高性能的懒加载与无限滚动实现
前端
韭菜炒大葱4 小时前
别等了!用 Vue 3 让 AI 边想边说,字字蹦到你脸上
前端·vue.js·aigc
StarkCoder5 小时前
求求你,别在 Swift 协程开头写 guard let self = self 了!
前端