DllPlugin
和DllReferencePlugin
提供了拆分包的方法,可以极大地提高构建时性能。术语DLL
代表动态链接库,它最初是由Microsoft引入的。.dll
为后缀的文件称为动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据cnpm i webpack webpack-cli html-webpack-plugin isarray is-promise -D
webpack.dll.config.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
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'
})
],
};
<!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>
src\index.js
let isarray = require("isarray");
console.log('isarray([1, 2, 3])=',isarray([1, 2, 3]));
package.json
"scripts": {
"dll": "webpack --config webpack.dll.config.js",
"build": "webpack --config webpack.config.js"
}
const webpack = require("webpack");
const webpackOptions = require("./webpack.dll.config");
const compiler = webpack(webpackOptions);
debugger
compiler.run((err, stats) => {
console.log(
stats.toJson({})
);
});
const webpack = require("webpack");
const webpackOptions = require("./webpack.config");
const compiler = webpack(webpackOptions);
compiler.run((err, stats) => {
console.log(
stats.toJson({})
);
});
<!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>
dist\utils.dll.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__;
})
});
dist\utils.manifest.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
}
}
}
}
dist\bundle.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;
})
});
plugins\DllPlugin.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;
plugins\DllEntryPlugin.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;
plugins\DllModuleFactory.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;
plugins\dependencies\DllEntryDependency.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;
plugins\DllModule.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;
plugins\LibManifestPlugin.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;
plugins\DllPlugin.js
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;
plugins\DllReferencePlugin.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;
plugins\ExternalModuleFactoryPlugin.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;
plugins\DelegatedModuleFactoryPlugin.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;
plugins\DelegatedModule.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;
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']
}
})
]
}
node_modules_autodll-webpack-plugin@0.4.2@autodll-webpack-plugin\lib\plugin.js
+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);
+ }
+ });