Open jyzwf opened 6 years ago
众所周知,webpack4 中添加了各个选项的默认值,那么我们来看看它内部是怎么设置的
进入 lib/webpack.js即可找到我们的 webpack 函数。
lib/webpack.js
首先 webpack 会先校验传入的options 是否合法,如果不合法就会直接给出报错。接着是定义一个 compiler 变量。如果传入的options是数组,意味着有多个配置对象,如下:
compiler
module.exports = [{ output: { filename: './dist-amd.js', libraryTarget: 'amd' }, entry: './app.js', mode: 'production', }, { output: { filename: './dist-commonjs.js', libraryTarget: 'commonjs' }, entry: './app.js', mode: 'production', }]
具体部分代码如下:
if (Array.isArray(options)) { compiler = new MultiCompiler(options.map(options => webpack(options))); } else if (typeof options === "object") { // 为配置设置初始值 options = new WebpackOptionsDefaulter().process(options); compiler = new Compiler(options.context); compiler.options = options; // 这里引入compiler.inputFileSystem new NodeEnvironmentPlugin().apply(compiler); if (options.plugins && Array.isArray(options.plugins)) { for (const plugin of options.plugins) { plugin.apply(compiler); } } // 执行environment,afterEnvironment两个hook compiler.hooks.environment.call(); compiler.hooks.afterEnvironment.call(); compiler.options = new WebpackOptionsApply().process(options, compiler); } else { throw new Error("Invalid argument: options"); }
这里只先讨论单对象的情况: 遂来看下 WebpackOptionsDefaulter 类,它继承自 OptionsDefaulter ,并且在里面设置默认值的方法主要就是调用父类 set 方法,并且上面的 process 方法也同样是调用父类。下面是具体的 set 方法:
WebpackOptionsDefaulter
OptionsDefaulter
set
process
set(name, config, def) { if (def !== undefined) { this.defaults[name] = def; this.config[name] = config; } else { this.defaults[name] = config; delete this.config[name]; } }
它主要是搜集各个选项的默认值和配置保存到 this.defaults和 this.config 字段中,详情如下: WebpackOptionsDefaulter
this.defaults
this.config
这里列举下config的枚举值:
config = [ make, call, // ['module','output','node','performance','optimization','optimization.runtimeChunk','resolve','resolveLoader'] 共9个 append, // 在 WebpackOptionsDefaulter 构造函数中未找到有该配置,??? ]
下面是全部收集完但不包含结合用户传进来的options产生的默认值收到的 this.default和 this.config:
options
this.default
// this.default { "entry": "./src", "context": "当前项目的目录", "target": "web", "module.unknownContextRequest": ".", "module.unknownContextRegExp": false, "module.unknownContextRecursive": true, "module.unknownContextCritical": true, "module.exprContextRequest": ".", "module.exprContextRegExp": false, "module.exprContextRecursive": true, "module.exprContextCritical": true, "module.wrappedContextRegExp": {}, "module.wrappedContextRecursive": true, "module.wrappedContextCritical": false, "module.strictExportPresence": false, "module.strictThisContextOnImports": false, "module.rules": [], "output.filename": "[name].js", "output.webassemblyModuleFilename": "[modulehash].module.wasm", "output.library": "", "output.libraryTarget": "var", "output.path": "/Users/bill/Documents/_webpack/learning/demo1/dist", "output.sourceMapFilename": "[file].map[query]", "output.hotUpdateChunkFilename": "[id].[hash].hot-update.js", "output.hotUpdateMainFilename": "[hash].hot-update.json", "output.crossOriginLoading": false, "output.jsonpScriptType": false, "output.chunkLoadTimeout": 120000, "output.hashFunction": "md4", "output.hashDigest": "hex", "output.hashDigestLength": 20, "output.devtoolLineToLine": false, "output.strictModuleExceptionHandling": false, "node.console": false, "node.process": true, "node.global": true, "node.Buffer": true, "node.setImmediate": true, "node.__filename": "mock", "node.__dirname": "mock", "performance.maxAssetSize": 250000, "performance.maxEntrypointSize": 250000, "optimization.removeAvailableModules": true, "optimization.removeEmptyChunks": true, "optimization.mergeDuplicateChunks": true, "optimization.providedExports": true, "optimization.splitChunks": {}, "optimization.splitChunks.chunks": "async", "optimization.splitChunks.minChunks": 1, "optimization.splitChunks.automaticNameDelimiter": "~", "optimization.splitChunks.name": true, "optimization.splitChunks.cacheGroups": {}, "optimization.splitChunks.cacheGroups.default": { "automaticNamePrefix": "", "reuseExistingChunk": true, "minChunks": 2, "priority": -20 }, "optimization.splitChunks.cacheGroups.vendors": { "automaticNamePrefix": "vendors", "test": {}, "priority": -10 }, "optimization.mangleWasmImports": false, "optimization.hashedModuleIds": false, "resolve.unsafeCache": true, "resolve.modules": [ "node_modules" ], "resolve.extensions": [ ".wasm", ".mjs", ".js", ".json" ], "resolve.mainFiles": [ "index" ], "resolveLoader.unsafeCache": true, "resolveLoader.mainFields": [ "loader", "main" ], "resolveLoader.extensions": [ ".js", ".json" ], "resolveLoader.mainFiles": [ "index" ] } // this.config { "devtool": "make", "cache": "make", "module": "call", "module.unsafeCache": "make", "module.defaultRules": "make", "output": "call", "output.chunkFilename": "make", "output.hotUpdateFunction": "make", "output.jsonpFunction": "make", "output.chunkCallbackName": "make", "output.globalObject": "make", "output.devtoolNamespace": "make", "output.pathinfo": "make", "node": "call", "performance": "call", "performance.hints": "make", "optimization": "call", "optimization.flagIncludedChunks": "make", "optimization.occurrenceOrder": "make", "optimization.sideEffects": "make", "optimization.usedExports": "make", "optimization.concatenateModules": "make", "optimization.splitChunks.hidePathInfo": "make", "optimization.splitChunks.minSize": "make", "optimization.splitChunks.maxAsyncRequests": "make", "optimization.splitChunks.maxInitialRequests": "make", "optimization.runtimeChunk": "call", "optimization.noEmitOnErrors": "make", "optimization.checkWasmTypes": "make", "optimization.namedModules": "make", "optimization.namedChunks": "make", "optimization.portableRecords": "make", "optimization.minimize": "make", "optimization.minimizer": "make", "optimization.nodeEnv": "make", "resolve": "call", "resolve.aliasFields": "make", "resolve.mainFields": "make", "resolve.cacheWithContext": "make", "resolveLoader": "call", "resolveLoader.cacheWithContext": "make" }
实例化完 WebpackOptionsDefaulter 后,就是结合用户传进来的 options,调用 process 方法来 生成一个完整的 options。 process 中会根据之前各个 set 中所对应的 config 值来处理各个配置项。
config
// process options = Object.assign({}, options); for (let name in this.defaults) { switch (this.config[name]) { case undefined: // 当配置项没有时,设置默认值 if (getProperty(options, name) === undefined) { setProperty(options, name, this.defaults[name]); } break; case "call": setProperty( options, name, this.defaults[name].call(this, getProperty(options, name), options) ); break; case "make": if (getProperty(options, name) === undefined) { setProperty(options, name, this.defaults[name].call(this, options)); } break; case "append": { let oldValue = getProperty(options, name); if (!Array.isArray(oldValue)) { oldValue = []; } oldValue.push(...this.defaults[name]); setProperty(options, name, oldValue); break; } default: throw new Error( "OptionsDefaulter cannot process " + this.config[name] ); } } return options
当没有配置项的时 先检验用户传进来的options是否有该选项,如果没有,就直接将默认值赋予该选项
当配置项值为 call 时: 该配置项的set时值均是 function ,并且 webpack 本身也为该选项对象的某些字段提供了默认值。它先将存在于用户配置中的选项先进行设置,如果没有设置的,才会利用默认值,且该函数的第一个参数是该选项的用户配置值,第二个为传入的options
call
function
webpack
当配置项值为 make 时: 该配置项的set时值均是 function ,默认第一个参数是传入的options
make
当配置项值为 append 时: 暂未见该使用场景,见到时再看
append
至此,webpack 设置最终的options就完成,接下来就是 Compiler 阶段
Compiler
众所周知,webpack4 中添加了各个选项的默认值,那么我们来看看它内部是怎么设置的
进入
lib/webpack.js
即可找到我们的 webpack 函数。首先 webpack 会先校验传入的options 是否合法,如果不合法就会直接给出报错。接着是定义一个
compiler
变量。如果传入的options是数组,意味着有多个配置对象,如下:具体部分代码如下:
这里只先讨论单对象的情况: 遂来看下
WebpackOptionsDefaulter
类,它继承自OptionsDefaulter
,并且在里面设置默认值的方法主要就是调用父类set
方法,并且上面的process
方法也同样是调用父类。下面是具体的set
方法:它主要是搜集各个选项的默认值和配置保存到
this.defaults
和this.config
字段中,详情如下: WebpackOptionsDefaulter这里列举下config的枚举值:
下面是全部收集完但不包含结合用户传进来的
options
产生的默认值收到的this.default
和this.config
:实例化完
WebpackOptionsDefaulter
后,就是结合用户传进来的options
,调用process
方法来 生成一个完整的options
。process
中会根据之前各个set
中所对应的config
值来处理各个配置项。当没有配置项的时 先检验用户传进来的options是否有该选项,如果没有,就直接将默认值赋予该选项
当配置项值为
call
时: 该配置项的set时值均是function
,并且webpack
本身也为该选项对象的某些字段提供了默认值。它先将存在于用户配置中的选项先进行设置,如果没有设置的,才会利用默认值,且该函数的第一个参数是该选项的用户配置值,第二个为传入的options当配置项值为
make
时: 该配置项的set时值均是function
,默认第一个参数是传入的options当配置项值为
append
时: 暂未见该使用场景,见到时再看至此,webpack 设置最终的options就完成,接下来就是
Compiler
阶段