总体过了一下后面的流程,发现Compiler模块确实不适合单独讲解,这里继续讲解后面的代码:
compiler.options = new WebpackOptionsApply().process(options, compiler);
这行代码与之前设置options默认值非常相似,但是复杂程度根本不是一个次元的。
这一节只能简单的看一眼内部到底有多少东西,整理后源码如下:
"use strict"; const OptionsApply = require("./OptionsApply"); // ...巨量插件引入 class WebpackOptionsApply extends OptionsApply { constructor() { super(); } process(options, compiler) { let ExternalsPlugin; compiler.outputPath = options.output.path; compiler.recordsInputPath = options.recordsInputPath || options.recordsPath; compiler.recordsOutputPath = options.recordsOutputPath || options.recordsPath; compiler.name = options.name; compiler.dependencies = options.dependencies; // 在默认参数配置中被设置为web if (typeof options.target === "string") { let JsonpTemplatePlugin; let NodeSourcePlugin; let NodeTargetPlugin; let NodeTemplatePlugin; switch (options.target) { case "web": JsonpTemplatePlugin = require("./JsonpTemplatePlugin"); NodeSourcePlugin = require("./node/NodeSourcePlugin"); compiler.apply( new JsonpTemplatePlugin(options.output), new FunctionModulePlugin(options.output), new NodeSourcePlugin(options.node), new LoaderTargetPlugin(options.target) ); break; // other case... default: throw new Error("Unsupported target ‘" + options.target + "‘."); } } else if (options.target !== false) { options.target(compiler); } else { throw new Error("Unsupported target ‘" + options.target + "‘."); } // options.output.library参数处理 if (options.output.library || options.output.libraryTarget !== "var") { /**/ } // options.output.externals参数处理 if (options.externals) { /**/ } let noSources; let legacy; let modern; let comment; // options.devtool => sourcemap || source-map if (options.devtool && (options.devtool.indexOf("sourcemap") >= 0 || options.devtool.indexOf("source-map") >= 0)) { /**/ } // options.devtool => sourcemap || source-map else if (options.devtool && options.devtool.indexOf("eval") >= 0) { /**/ } // 加载模块并触发entry-option事件流 compiler.apply(new EntryOptionPlugin()); compiler.applyPluginsBailResult("entry-option", options.context, options.entry); // 疯狂加载插件 compiler.apply( /**/ ); // options.performance参数处理 if (options.performance) { /**/ } // 继续加载插件 compiler.apply(new TemplatedPathPlugin()); compiler.apply(new RecordIdsPlugin()); compiler.apply(new WarnCaseSensitiveModulesPlugin()); // options.performance参数处理 if (options.cache) { /**/ } // 触发after-plugins compiler.applyPlugins("after-plugins", compiler); if (!compiler.inputFileSystem) throw new Error("No input filesystem provided"); // 给compiler.resolvers设置值 compiler.resolvers.normal = ResolverFactory.createResolver(Object.assign({ fileSystem: compiler.inputFileSystem }, options.resolve)); compiler.resolvers.context = ResolverFactory.createResolver(Object.assign({ fileSystem: compiler.inputFileSystem, resolveToContext: true }, options.resolve)); compiler.resolvers.loader = ResolverFactory.createResolver(Object.assign({ fileSystem: compiler.inputFileSystem }, options.resolveLoader)); // 触发after-resolvers事件流 compiler.applyPlugins("after-resolvers", compiler); return options; } } module.exports = WebpackOptionsApply;
这个模块除去父类引入,其余插件光顶部引入就有34个,简直就是插件之王。
略去具体插件内容,先看流程,父类其实是个接口,啥都没有:
"use strict"; class OptionsApply { process(options, compiler) {} } module.exports = OptionsApply;
接下来是一个唯一的主方法process,总结下流程依次为:
1、根据options.target加载对应的插件,如果配置文件没有配置该参数,则在WebpackOptionsDefaulter模块会被自动初始化为web。
2、处理options.output.library、options.output.externals参数
3、处理options.devtool参数
4、加载EntryOptionPlugin插件并触发entry-option的事件流
5、加载大量插件
6、处理options.performance参数
7、加载TemplatePathPlugin、RecordIdPlugin、WarnCaseSensitiveModulesPlugin插件
8、触发after-plugins事件流
9、设置compiler.resolvers的值
10、触发after-resolvers事件流
如果按类型分,其实只有两种:加载插件,触发事件流。
事件流的触发类似于vue源码里的钩子函数,到特定的阶段触发对应的方法,这个思想在Java的数据结构源码中也被普通应用。
模块中的参数处理如果该参数比较常用,那么就进行分析,其余不太常用的就先跳过,按顺序依次讲解。
这里的options经过默认参数模块的加工,丰富后如下:
{ "entry": "./input.js", "output": { "filename": "output.js", "chunkFilename": "[id].output.js", "library": "", "hotUpdateFunction": "webpackHotUpdate", "jsonpFunction": "webpackJsonp", "libraryTarget": "var", "path": "D:\\workspace\\doc", "sourceMapFilename": "[file].map[query]", "hotUpdateChunkFilename": "[id].[hash].hot-update.js", "hotUpdateMainFilename": "[hash].hot-update.json", "crossOriginLoading": false, "chunkLoadTimeout": 120000, "hashFunction": "md5", "hashDigest": "hex", "hashDigestLength": 20, "devtoolLineToLine": false, "strictModuleExceptionHandling": false }, "context": "D:\\workspace\\doc", "devtool": false, "cache": true, "target": "web", "module": { "unknownContextRequest": ".", "unknownContextRegExp": false, "unknownContextRecursive": true, "unknownContextCritical": true, "exprContextRequest": ".", "exprContextRegExp": false, "exprContextRecursive": true, "exprContextCritical": true, "wrappedContextRegExp": {}, "wrappedContextRecursive": true, "wrappedContextCritical": false, "strictExportPresence": false, "strictThisContextOnImports": false, "unsafeCache": true }, "node": { "console": false, "process": true, "global": true, "Buffer": true, "setImmediate": true, "__filename": "mock", "__dirname": "mock" }, "performance": { "maxAssetSize": 250000, "maxEntrypointSize": 250000, "hints": false }, "resolve": { "unsafeCache": true, "modules": ["node_modules"], "extensions": [".js", ".json"], "mainFiles": ["index"], "aliasFields": ["browser"], "mainFields": ["browser", "module", "main"], "cacheWithContext": false }, "resolveLoader": { "unsafeCache": true, "mainFields": ["loader", "main"], "extensions": [".js", ".json"], "mainFiles": ["index"], "cacheWithContext": false } }
除去entry与output.filename,其余的参数全部是填充上去的,因为后面的流程会检测参数,所以这里先列出来。
这一节先这样,具体内容后面进行详细讲解。