1、事件(插件)机制
webpack采用事件驱动的方式打包web应用,其事件机制主要依靠一个很小的核心库tapable来实现。tabable库主要实现了事件定义、事件处理程序注册、事件触发等基本的事件系统功能。
1-1、事件对象---Hook
在tapable库,事件对象被抽象为Hook,Hook主要结构如下:
class Hook {
construnctor(args = [], name = undefined) {
// 存储事件处理程序
this.taps = []
// 存储hook对象的拦截器,拦截器会分别在注册与触发事件处理程序时,
// 对事件处理程序进行一系列前置处理
this.interceptors = []
// 下面三个属性,表示触发hook,并传入参数args;不同的触发方式对应相应的hook的行为
// fn表示hook的行为为正常函数, asyncFn表示hook的行为为回调函数, prmiseFn
// 表示hook的行为为promise
this.call = fn;
this.callAsync = asyncFn
this.promise = promiseFn
}
// 注册事件处理函数的三种方式,
// options : string | { name: string, before: boolean, after: boolean },
// name为事件处理函数的标识,fn为事件处理函数, after, before,用于调度触发hook时,
// hook关联的函数执行顺序
tap(options, fn) {}
tapAsync(options, fn) {}
tapPromise(options) { /* function body */ }
// 添加拦截器对象
intercept(interceptor) { /* function body */}
}
此外,根据hook以一定的调用方式执行过程中事件处理函数的不同行为,Hook可以分为几下几类:
-
基本型:事件处理函数按照某种调用方式执行;
-
瀑布型:事件处理程序按照某种调用方式执行,但是上一个函数的返回值会传递到下一个函数而替换第一个参数;
-
保释型:事件处理程序按照某种调用方式执行,但若某个函数缺省返回值,则立即返回;
-
循环型:事件处理程序按照某种调用方式执行,若在此过程中若某个函数返回值不是undefined,则回到第一个函数,再次开始执行;
1-2、事件处理函数---Tap
tap结构如下:
type tap {
name: string, // 事件处理函数标识符
fn: // 事件处理函数
type: string // 事件处理函数的注册方式,"sync" | "async" || "promise"
before: array,
stage: number,
}
1-3、拦截器---Interceptor
interceptor主要结构如下:
class interceptor {
// 1)*** 拦截器的功能函数:在hook注册与调用事件处理函数时的拦截行为 ***
// 在使用hook.tap、hook.tapAsync、hook.tapPromise注册事件处理函数时的拦截行为,
// 不能修改数参数tap
tap(tap) {},
// 在使用hook.tap、hook.tapAsync、hook.tapPromise注册事件处理函数时的拦截行为,
// 可以修改参数tap
register(tap) {},
// 在hook触发是的拦截行为,在hook的事件处理程序之前执行
call(...args),
// 在LoopHook中的每轮拦截行为
loop(...args) {}
// 2)*** 拦截器的结果函数:如何处理拦截结果 ***
// 拦截器结果的处理函数,result与done只执行一个;
done() {}
result() {}
// 拦截过程中出现错误的处理函数
error() {}
}
1-4、事件处理函数工厂---HookCodeFactory
HookCodeFactory主要功能是通过new Function(...args, functionBody) 将与hook关联的事件处理函数与拦截器,以不同的调用方式(顺序、并行、循环) 组合动态生成一个具有不同触发方式(call,callAsync,promise)的不同类型(sync,async)的hook触发函数;
1-5、Hook类型
通过组合Hook的事件处理函数的不同行为与hook的不同类型 , tapable核心库暴露以下九类Hook:
const (
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook // 解释为 异步hook函数, 允许tap为异步函数顺序执行, 且参数会传递
} = require("tapable")
2、基本流程
webpack整体的构建流程大致可以分:
-
实例化compiler,加载指定插件,设置编译环境,加载内部插件,设置解析器;
-
运行compiler,创建构建过程complication,并对complication进行优化;
2-1、实例化compiler
实例化complier的代码,在webpack源代码的webpack.js文件中,简化后的代码如下:
const createCompiler = rawOptions => {
const options = getNormalizedWebpackOptions(rawOptions)
applyWebpackOptionsBaseDefaults(options)
// 1)创建compiler实例
const compiler = new Compiler(options.context, options)
applyWebpackOptionsDefaults(options)
// 2)初始化config里的插件
if (Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
if (typeof plugin === "function") {
plugin.call(compiler, compiler)
} else {
plugin.apply(compiler)
}
}
}
// 3)触发enviromentHook
compiler.hooks.environment.call()
// 4) 触发afterEnvironmentHook
compiler.hooks.afterEnvironment.call()
// 5)触发entryOptionHook ---> 实例化各种内部插件 ---> 触发afterPluginsHook --->
// 设置解析器---> 触发afterResolversHook
new WebpackOptionsApply().process(options, compiler)
// 6)触发initializeHook
compiler.hooks.initialize.call();
return compiler
};
// 单个配置对象
const webpack = (options, callback) => {
const create = () => {
// 检测并获取有效配置
[options].every(webpackOptionsSchemaCheck)
getValidateSchema()(webpackOptionsSchema, options)
const webpackOptions = options
compiler = createCompiler(webpackOptions)
watch = webpackOptions.watch
watchOptions = webpackOptions.watchOptions || {}
return {compiler, watch, watchOptions }
}
const { compiler, watch, watchOptions } = create()
if(callback) {
if (watch) {
compiler.watch(watchOptions, callback)
} else {
compiler.run((err, stats) => {
compiler.close(err2 => {
callback(err || err2, stats)
})
})
}
}
return compiler
}
2-2、运行compiler
compiler类的伪代码如下:
class Compiler {
constructor() { /* function body */}
run() {
this.readRecords()
this.compile()
}
readRecords() { /* function body */ }
emitAssets(compilation) { /* function body */ }
emitRecords(compilation) { /* function body */ }
onCompiled(compilation) {
this.emitAssets(compication)
this.emitRecords(complication)
}
createNormalModuleFactory() { /* function body */ }
createContextModuleFactory() { /* function body */ }
newCompilationParams() {
const params = {
normalModuleFactory: this.createNormalModuleFactory()
contextModuleFactory: this.createContextModuleFactory()
}
return params
}
newCompilation(params) { /* function body */ }
compile() {
const params = this.newCompilationParams()
const compilation = this.newCompilation(params)
this.hooks.make.callAsync(compilation)
this.onCompiled(compilation)
}
}
compiler运行过程,可以分为以下几个阶段:
-
readRecords:若存在构建记录,则读取构建记录,用于分包缓存优化(未设置recordsInputPath时直接返回);否则,直接返回;
-
compile:解析并构建模块,生成构建过程compilation实例,并优化构建过程;
-
实例化normalModuleFactory与contextModuleFactory,生成compilationParams;
-
传入compilationParams,创建编译过程compilation实例,从入口文件递归添加和构建模块;
-
触发make的hook,执行所有监听make的插件;
-
调用complication.finish,报告构建过程中的错误与警告;
-
调用complication.seal,优化构建过程;
-
emitAssets:将构建结果写入到输出目录;
-
emitRecords:若已设置recordsInputPath,将构建记录写入到构建记录;否则直接返回;
2-3、插件系统
webpack的插件系统是建立在事件机制与生命周期(或内部事件)上的,用户只需要在内部事件(webpack运行过程中各个组成部分的生命周期节点)上使用tap函数(tap、tapAsync、tapPromise)添加具体的插件功能函数,插件函数(事件处理函数)会在内部特定生命周期节点被触发时而执行。如:
// 定义插件函数
function MyPlugin() {
console.log('Synchronously tapping the compile hook.')
}
// 在特定生命周期注册插件函数
compiler.hooks.compile.tap(MyPlugin.name, MyPlugin);
2-4、生命周期
webpack的生命周期由运行过程中各个组成部分的生命周期节点构成,其组成部分及各个部分内的不同阶段的生命周期节点如下:
2-4-1、compiler的生命周期
1)初始化阶段
-
先在webpack.js中创建compiler实例,加载配置内插件后触发 :environment、afterEnvironment;
-
在执行webpackOptionsApply.prcocess中,执行EntryOptions插件后触发entryOption;加载内部插件后触发afterPlugins,在设置resolver配置后触发afterResolvers;
-
最后,在webpack.js中,返回compiler前触发initialize;
2)构建构成过程阶段
- 开始运行compiler阶段:在compiler.js中,当运行compiler时,依次触发beforeRun、run、watchRun(运行compiler.watch创建watcher时,在watch.js中触发)
- 运行构建过程中:在compiler.js中,当创建compilationParams时触发分别触发normalmodulefactory和contextModuleFactory;之后,触发beforeCompile、compile,然后在创建compilation实例中依次触发thisCompilation、compilation、make、afterCompile;
- 之后生成assets阶段:依次触发shouldEmit、emit、assetEmitted、afterEmit;
- 之后达到构建结果状态,触发done或failed
2-4-2、compilation的生命周期
1)构建阶段
构建阶段:在创建构建队列(build queue)中触发buildModule、succeedModule或failedModule;在创建再次构建队列(rebuild queue)中触发rebuildModule、finishRebuildingModule;构建完所有模块且无错误时,触发finishModules
2)优化阶段
-
Chunks生成:在chunks的生成过程前后触发 beforeChunks(2811行),afterChunks(2943行);见compilation.js;
-
模块优化:在模块的优化过程前后触发optimizeModules与afterOptimizeModules;
-
Chunk优化:在Chunk的优化过程前后触发optimizeChunks与afterOptimizeChunks;
-
Tree优化:在Tree的优化过程前后触发optimizeTrees与afterOptimizeTrees;
-
ChunkModules优化:在ChunkModules的优化过程前后触发optimizeChunkModules与afterOptimizeChunkModules;
-
检测是否存储记录:触发shouldRecord;
-
ModuleId生成:在ModuleId的生成过程前后触发reviveModules、beforeModuleIds、moduleIds、optimizeModuleIds、afterOptimizeModuleIds;
-
ChunkId生成:在ChunkId的生成过程前后触发reviveChunks、beforeChunks、chunksIds、optimizeChunks、afterOptimizeChunks;
-
存储模块和Chunk记录:当shouldRecord为true时,依次触发recordModules、recordChunks;
-
哈希模块和构建过程compilation:在模块的哈希过程前后触发beforeModuleHash与afterModuleHash;在构建过程的哈希过程前后触发beforeHash与afterHash;
-
存储记录哈希与构建过程的信息:触发recordHash与record;
-
生成ModuleAssets与ChunkAssets:在ModuleAssets的生成过程前后触发beforeModuleAssets、moduleAsset;在ChunkAssets的生成过程前后触发shouldGenerateChunkAsset、beforeChunkAssets、chunkAsset;
-
Assets优化:在Assets的优化过程前后触发optimizeChunkAssets、afterOptimizeChunkAssets、optimizeAssets、processAssets、afterProcessAssets;
3、配置
3-1、entry
// 应用入口与打包入口, 基本规则: 一个入口文件对应一个html文件,单页面应用只有一个入口文件
module.exports = {
// Entry descriptor object
main: {
dependOn: [], // 当前入口依赖的其他入口
filename: "bundled-filename", // 指定打包后的文件名,会覆盖output.filename
import: './index.js', // 待打包文件的路径
library: { // 库选项,指定当前入口打包作为一个库时的使用名称、暴露方式、与输出的字段
name: "library-name",
type: "_", // 定义库的暴露方式
export: ["a", "b"]
},
runtime: "bundled-runtime-chunk-name",
publicPath: "", // output.path的 url in html page
},
3-2、output
const path = require('path')
const allOutputOptions = { // filename for asset
asyncChunks: true, // 是否创建异步chunk用于按需加载
charset: true, // 是否使webapck在<script>标签上添加
charset='utf-8'
assetModuleFilename: '[hash][ext][query]', // module, non-initial chunk name
// name: entry name, id: chunk id, contenthash,
filename: 'dirname/[name][contenthash][id].bundle.js', // 设置entry对应的打包文件名称格式
path: "output-directory", // 设置打包文件的输出目录
chunkFilename: "[id].js", // 设置按需加载的chunk文件名称格式
chunkFormat: "array-push", // 'array-push' (web/WebWorker), 'commonjs' (node.js), 'module' (ESM)
// 加载chunk的方法,false: 使按需加载失效, 'jsonp' (web), 'import' (ESM),
//'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js)
chunkLoading: false,
crossOriginLoading: false, // 是否允许跨域加载,只有 chunkLoading = 'jsonp'时有效
hotUpdateGlobal: "", //
chunkLoadTimeout: 120000, // chunk请求超时
chunkLoadingGlobal: "", // 加载chunk时的webpack全局变量
clean: { dry: false, keep: "" }, // 重新打包时,是否清理上次的打包文件
compareBeforeEmit: false, // 生成打包文件 前,检查是否存在 未发生变化的文件; webpack默认:不会再写输出未发生变化的文件
// 设置 devtool时的输入选项
devtoolNamespace: "", // 默认等于 output.uniqueName
devtoolModuleFilenameTemplate: "", //
devtoolFallbackModuleFilenameTemplate: "", // Olny devtool = 'source-map'
sourceMapFilename: "[name][id][chunkhash]", // 设置 source-map的名称
sourcePrefix: "", // 设置输出文件的前缀,可以进行缩进美化代码增加可读性
// 当 entry 为函数时,需要设置
enabledChunkLoadingTypes: [], // ['jsonp' | 'import-scripts' | 'require' | 'async-node' ]
enabledLibraryTypes: [], // ['module', 'commonjs', 'umd'] 详细见
output.library.type
// WASM module
enabledWasmLoadingTypes: ['fetch'], // wasm
wasmLoading: "fetch", //
// globalObejct: "self",
// hash
hashDigest: 'hex', // hash摘要的编码方式
hashDigestlength: 20, // 截取hash值的前n位
hashFunction: "md4", // 设置hash算法
hashsalt: "", // hash salt
// Hot update
hotUpdateChunkFilename: "[id][fullhash].hot-update.js", // 通常使用默认值
hotUpdateMainFilename: "[runtime][fullhahs].hot-update.js", // 通常使用默认值
// iife:立即执行函数
iife: true, // 是否使用iife封装打包后的代码
// 生成代码的包含的ES features
environment: {
arrowFunction: true,
bigIntLiteral: false,
const: true,
destructuring: true,
dynamicImport: false,
forOf: true,
module: false,
optionalChaining: true,
templateLiteral: true,
},
// importFunctionName
importFunctionName: "__import__", // name for import()
uniqueName: "", // 打包文件成库时的选项
// 默认等于 output.library.name, 将用于output.chunkLoadingGlobal生成唯一global
library: {
name: 'library-name',
type: 'library-module-type',
export: ['default', 'subModeule'], // 模块暴露那些 exports字段
auxiliaryComment: "Inserted Comment within exports wrapper",
umdNamedDefine: true, // For type = 'umd'
},
// module: false, // 输出JS为module类型 experimental feature.
scriptType: false, // 设置script标签的type属性,false:表示禁止使用自定义script type异步加载脚本
pathinfo: false, // 打包文件中是否包含打包文件里包含的模块的注释 true for developement; false for production
// publicPath: 输出文件在HTMl中的url的路径目录,
// It automatically determines the public path from either `import.meta.url`,
//`document.currentScript`, `<script />` or `self.location`.
publicPath: 'auto',
// publicPath: 'https://cdn.example.com/assets/', // CDN (always HTTPS)
// publicPath: '//cdn.example.com/assets/', // CDN (same protocol)
// publicPath: '/assets/', // server-relative
// publicPath: 'assets/', // relative to HTML page
// publicPath: '../assets/', // relative to HTML page
// publicPath: '', // relative to HTML page (same directory)
strictModuleExceptionHandling: true, // 按照ES规范处理模块加载过程中的错误,会有性能代价
}
const typicalOutputOptions = {
path: path.resolve(__dirname, 'dist'),
filename: 'dirname/[name][contenthash][ext]',
chunkFilename: '[name][contenthash][chunkhash][id]',
assetModuleFilename: "[hash][ext][query]",
sourceMapFilename: '[name].map',
publicPath: path.resolve(__dirname, 'public'),
clean: true,
library: {
name: 'custom-library-name',
type: 'module',
export: ['default', 'subModule']
}
}
module.exports = typicalOutputOptions
3-3、module
const allModuleOptions = {
generator: {/* module-type: rule.generator */ }, // 设置不同模块类型对应的生成器
parser: {/* module-type: rule.parser */ }, // 模块不同类型对应的解析器
noParse: [], // 忽略对 未采用模块化方式的文件(不包含import, require,define等模块化语句) 的递归解析处理,提高构建性能;
unsafeCache: false, // webpack5的缓存策略,默认下:开启cache选项且在node_modules下的依赖会被缓存,即true; 否则为false;
rules: [ // Rule: 更改模块的创建方式,可对模块应用loader,或更改模块的解析器
{ /* Rule */
// 设置loader类别,忽略等于normal; 所有loader会经历两个阶段:Pitch(不需要前面loader生成的结果)与Normal;
// Pitch阶段:loader从左向右依次进行;Normal阶段:loader从右向左依次进行
enforce: '',
// Resource condition: 被导入的模块,即 包含 export 的模块
test: "", // 匹配模块类型
exclude: [], // 匹配的文件不会经过test校验
include: [], // 匹配的文件必须经过test校验
resource: "", // 设置 被导入模块 的匹配规则
recourceQuery: "", // 设置 被待query的导入模块的query规则
// Issuer condition:依赖其他模块的模块,即包含 import 的模块
issuer: "", // 设置 发起模块导入请求的模块 的匹配规则
issuerLayer: "", // 设置issuer的layer匹配规则
// Module
layer: "", // 设置模块被放置的层
scheme: "", // 设置 模块的uri的协议
mimetype: "", // 设置 模块的uri媒体类型
type: "", // 设置 模块类型,可绕过默认规则与默认模块行为,来设置自定义loader
sideEffects: "", // Tree Shaking
use: [
{ // UseEntry: loader object
loader: "loader-name",
options: {/* loadr options */ }
}
],
rules: {}, // 嵌套规则
oneOf: [], // 规则数组,选择满足条件的第一条规则,
// generator, parser, resolve
generator: {
dataUrl: false, // true时,为 {encoding: 'base64' | false, mimitype }
emit: fasle, // 设置 是否生成Asset
filename: "", // 同 output.assetModuleFilename,只对 模块类型为 asset 与 asset/resource的模块有效
outputPath: "", // 相对 output.path的目录
publicPath: "", // 同 output.publicPath,但是针对 Asset module
},
parser: { // 解析器选项
parse: () => { }, // 解析器的解析函数,将toml,yaml等非JSON文件导入成JSON时很有用
dataUrlCondition: { maxSize: 4096 }, // 设置 可转换成内联的dataURl格式的模块最大值;低于此值,将会被内嵌为dataUrl格式
},
resolve: {} // 模块级的config.resolve
}
]
}
const typicalModuleOptions = {}
module.exports = typicalModuleOptions
3-4、resolve
const allResolveOptions = {
conditionNames: [/* 'require', 'node' */], // 条件查找package.json中的exports的
modules: [], // 按序查找 模块解析目录,绝对路径与相对路径,相对路径会向上查找,
alias: { /** alternate-path: origin-path */ },
exportsFields: [/*'exports'*/], // 1) 包中package.json中的字段,
mainFields: [/*'browser', 'module', 'main'*/], // 2) 解析含有package.json的目录时, 目录内package.json中的字段
mainFiles: [/* 'index' */], // 3)解析不含有package.json的目录时,或在2)中未找到有效文件时,查找该目录内的文件名称
extensions: [/* '.js', '.json' */], // 4)当文件按不含扩展名时,依次推断文件的扩展名
extensionAlias: { /* '.js': ['.ts', '.js'], '.mjs': ['.mts', '.mjs'] */ },
restrictions: /**[string, RegExp] */"", // 解析限制列表,只有被指定的模块路径可以被解析
// Cache
unsafeCache: [/** RegExp */], // webpack4的缓存策略 条件缓存, true:缓存所有
cachePredicate: /* function({ path , request} boolean) */ (module) => true, // 条件缓存模块请求
// 其他
plugins: [], // 模块解析插件
// import a from "a.js" , 设置true,将a.js 优先使用 ./a.js进行查找,否则在node_modules
preferRelative: /* Boolean: true | false */ true,
preferAbsolute: false,
aliasFields: [], // 详情见:https://github.com/defunctzombie/package-browser-field-spec
byDependency: {} // 为特定类型的模块,指定解析模块的选择项,
}
const typicalResolveOptions = {
modules: [],
alias: { /** alternate-path: origin-path */ },
exportsFields: [/*'exports'*/],
mainFields: [/*'browser', 'module', 'main'*/],
mainFiles: [/* 'index' */],
}
module.exports = typicalResolveOptions
3-5、dev-server
const path = require('path');
// 设置webapck-dev-server的选项
const allDevServerOptions = {
allowedHosts: /* ['.host.com', 'host2.com'], */[], // 可以访问服务的域名的白名单
bonjour: /* { type: 'http', protocol: 'udp' }*/ {}, // ???
client: {
logging: "", // 设置浏览器中的 log level
overlay: /** errors: boolean, warnings: boolean } */ {}, // 设置错误或警告发生时,是否全屏覆盖,,广播天下
progress: /** boolean */ false, // 打印编译进度
reconnect: /** true | bnumber */ true, // dev-server重连client的次数 ,true --->无限次
webSocketURL: {
protocol: 'ws',
password: 'dev-server',
username: 'webpack',
hostname: '0.0.0.0',
port: 8080,
pathname: '/ws',
},
webSocketTransport: /* 'ws' | 'sockjs' */'ws',
},
websocketServer: {
options: {
path: "/my/custom/path/to/web/socket/server"
}
},
compress: true, // 是否开启gzip压缩
// 设置webpack-dev-middleware的选项
devMiddleware: {
index: true,
mimeTypes: { "text/html": ["phtml"] },
publicPath: "/publicPathForDevServe",
serverSideRender: true,
writeToDisk: true,
},
static: {
directory: path.join(__dirname, 'public'),
// 下列均为默认值
staticOptions: {
dotfiles: "ignore", // 设置如何处理以 "."开头的文件或目录,
etag: true, // 是否生成etag
extensions: false /* ['html', 'js'] */, // 设置文件扩展,当未找到文件时,依次收索指定扩展的文件
fallthrough: true, // 是否忽略中间件错误, true忽略,false返回错误
index: "index.html", // 指定目录索引文件,
lastModified: true, // 是否设置 Last-Modified header
maxAge: 0, // 是否设置 Cache-Control header的'max-age'属性
redirect: true, // 当路径为目录且以'/'结尾时, 重定向到该目录下
setHeaders: /** function(response, path, stat) */ () => { }, //
},
publicPath: ['/static-public-path-one/', '/static-public-path-two/'],
serveIndex: {},
watch: true, // 默认tre
},
server: {
type: "https",
options: /* { ca, cert, key, pfx, cert, requestCert } */{}, // 使用https协议, dev-server默认http,也可以使用数字证书
},
headers: {}, / add header to all responses
historyApiFallback: false, // history模式时需要
watchFiles: false,
magicHtml: false, // 是否启用html-magic
// middleware
onAfterSetupMiddleware: () => { }, // 在内部全部Middleware调用**后**调用
onBeforeSetupMiddleware: () => { }, // 在内部全部Middleware调用**前**调用
// local serve
onListen: function (devServer) { }, // 开始监听端口时,调用
open: true, // 是否服务启动后,打开浏览器
host: 'local-ip', // 指定本地主机域名
port: 9000, // 服务端口
hot: true, // 是否开启热更新
liveReload: false, // 检测到变化时,重载/刷新页面
// Proxy
proxy: [
{
context: ['/auth', '/api'],
target: 'http://localhost:3000', //
// { '^/old/api': '/new/api' }
pathRewrite: { '^/api': '' }, // 转发请求时,不会在路径上添加 '/api'
ws: false, // 是否代理websocket连接
secure: false, // 是否开启验证证书
changeOrigin: true, // 是否支持虚拟主机站点
timeout: 5000, // 请求超时
proxyTimeout: 0, // 代理超时
// bypass:选择性进行代理
bypass(request, response, proxyOptions) {
return 'path'
}
}
],
setupExitSignals: true, // 允许退出
}
const typicalDevServerOptions = {}
module.exports = typicalDevServerOptions
3-6、optimization
const allOptimizationOptions = {
// Module and Chunk
chunkIds: '', // chunk id 算法选择
moduleIds: '', // module id 算法选择
// 是否 将模块图中的模块片段拼接成一个模块 默认:production: true, 其他为false concatenateModules: true,
removeEmptyChunks: true, // 是否 检测并移除空模块
removeAvailableModules: true, // 同flagInculdedChunks,但对象为module
flagInculdedChunks: true, // 是否 检测并标记子chunk, 当chunk已加载时,子chunk不会被加载, 默认:production: true, 其他为false
mergeDupicateChunks: true, // 是否合并重复块
// 默认 :false, 表示每个chunk包含runtime; true| mutiple, 生成一个chunK间共享的包含runtime的chunk; single, 生成一个chunK间共享的runtime文件
runtimeChunk: false,
minimise: true, // 是否 使用默认的TersePlugin或
minimizer指定的Plugin压缩bundle
minimizer: {}, //
emitOnErrors: true, // 是否 在即使asset编译错误,仍然产生文件,可能会导致运行时错误
innerGraph: false, // 是否 对未使用的exports进行分析
// 是否生成相对路径的记录; 默认: false, 当设置recordsPath, recordsInputPath, recordsOutputPath中任一个时,为true
portableRecords: false,
// ***Exports***
// 是否使用较短的name表示输出字段默认: true in production with two chars, for long cache
mangleExports: true,
mangleWasmImports: true, // 同上,但针对wasm
providedExports: true, // 是否 将 export * from ...转化为更具体的输出
usedExports: true, // true | false | 'global' //
nodeEnv: false, // 是否 设置并覆盖process.env.NODE_ENV的值
// 是否 当Asset已生成哈希后,再hash一次; 设置false,使用内部数据计算hash,可能会导致内容未变化时hash值改变
realHashContent: true,
sideEffects: true, // 是否 识别package.json或rules中的sideEffects,移除未使用且没有副作用的输出模块
// 使用动态导入方式生成的chunk的分块选项
splitChunks: {
// 在下列情况下,webpack会默认分块:
// 1) 当ChunK可以共享, 或 模块来自node_modules目录中时;
// 2) 在进行 min+gzp前,chunk大小超过20KB
// 3) 当按需加载chunk时,并行请求最大值, 小于或等于 30
// 4) 初始页面加载时,并行请求最大值, 小于或等于 30
// 尝试 满足 3) 4)时 , 优先采用 使块的大小更大的分块方式
// 分隔条件
automaticNameDelimiter: '', // 自动命名分隔符,用于命名chunk,默认 origin + name
chunks: 'async', // 设置进行优化的chunk类型, 'initial' | 'async' | 'all'
maxAsyncRequests: 30, // 设置按需加载的最大异步请求数 maxInitialRequests: 30,
defaultSizeTypes: ['javascript', 'unknown'], // 最小
minChunks: 1, // 设置 模块至少被chunk共享的次数,低于此值module不会被分隔成块
maxSize: /* n: number */0, // 设置 可以被分成成chunk的最大尺寸;
maxInitialSize: /* n: number */0, // 设置 按需entry模块可以被分成成chunk的最大尺寸;
maxAsyncSize: /* n: number */0, // 设置 按需加载的模块可以被分成成chunk的最大尺寸;
layer: "", // 筛选layer复合规则的模块进行分组缓存 // 设置 被分割的 chunk的属性
minRemainingSize: /* n: number */ 0, // 设置 分割后chunk至少还有 n 字节
minSizeReduction: /* n: number */ 0, // 设置 当从chunk A 中分隔出的 chunk B使,chunk A至少减小 n 字节
hidePathInfo: false, // 设置 是否暴露通过 maxSize 分割时的被分隔块的路径信息,
name: '', // 设置 chunk name // 设置分隔出的chunk属性
minSize: 20000, // 设置 模块可以被分隔成chunk的最小尺寸,单位bytes, 最大为 maxSize
// ***强制分隔***
// 忽略 minRemainingSize, maxAsyncRequests, maxInitialRequests
// 设置,强制分隔chunk; 当设置时,大于 n 的chunk,被强制分隔
enforceSizeThreshold: /* n: number */ 50000,
usedExports: {}, // 同 optimization.usedExports
// group: can interit or override any options fom optimization, and has below specific options
cacheGroups: {
groupOne: {
test: '', // 设置当前缓存组的匹配规则
priority: /* n: number */ 0, // 一个模块可以属于多个缓存组,优化优先应用到priority高的爨村组;
reuseExistingChunk: true, // 若当前chunk包含main bundle中的模块,则main bundle
type: "", // 通过模块类型进行缓存
filename: "" // 覆盖 output.filename for initial entry
},
default: false, // 关闭默认缓存,默认缓存组的 priority = -20 }
},
}
onst typicalOptimizationOptions = {}
module.exports = typicalOptimizationOptions
3-7、cache
const allCacheOptions = {
type: "filesystem",
// 设置 缓存方式: 'memory' | 'filesystem',
buildDependencies: {},
// ???
// only for type = 'filesystem'
allowCollectingMemory: false, // 回收反序列化中未使用的内存; 默认 false for production, true for memoey
cacheDirectory: "", // 缓存的基目录,默认 node_modules/.cache/webpack
cacheLocation: "", // 缓存目录, 默认 path.resolve(cache.cacheDirectory, cache.name).
compression: "", // 缓存文件的压缩类型, 'gzip' | 'brotli'
hashAlgorithm: "", // 哈希生成算法, 默认 'md4'
idleTimeout: 60000, // 缓存有效时长
idleTimeoutAfterLargeChanges: 1000, // 当有大的改变被检测到时,的缓存有效时长,
idleTimeoutForInitialStore: 5000, //
maxAge: 5184000000, // 未使用的缓存记录的缓存时长, 默认一个月
memoryCacheUnaffected: true,
name: "", // 缓存的名称,默认 `${config.name}-${config.mode}`
profile: false, // 追踪并记录缓存信息
store: "pack", // 使用一个文件缓存所有
version: "", // 缓存的版本
// only for type = 'memery'
cacheUnaffected: "", // 缓存未发生变化的模块
maxGenerations: 1, // 未使用的缓存记录的最大编译次数 //
managedPath: [],
}
const typicalCacheOptions = {}
module.exports = typicalCacheOptions