.15-浅析webpack源码之WebpackOptionsApply模块之插件王中王

Posted QH-Jimmy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.15-浅析webpack源码之WebpackOptionsApply模块之插件王中王相关的知识,希望对你有一定的参考价值。

  总体过了一下后面的流程,发现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,其余的参数全部是填充上去的,因为后面的流程会检测参数,所以这里先列出来。

 

  这一节先这样,具体内容后面进行详细讲解。

以上是关于.15-浅析webpack源码之WebpackOptionsApply模块之插件王中王的主要内容,如果未能解决你的问题,请参考以下文章

.6-浅析webpack源码之validateSchema模块

.7-浅析webpack源码之WebpackOptionsDefaulter模块

.11-浅析webpack源码之Storage模块

.12-浅析webpack源码之NodeWatchFileSystem模块总览

.32-浅析webpack源码之doResolve事件流

.5-浅析webpack源码之入口函数