乐趣区

浅析webpack源码之入口函数webpack.js详解(四)

我们看到引入了
对 webpack.js
const validateSchema = require(“./validateSchema”);
const webpackOptionsSchema = require(“../schemas/WebpackOptions.json”);
const webpackOptionsValidationErrors = validateSchema(
webpackOptionsSchema,
options
);

进行分析
webpackOptionsSchema 是一个很大的 json 文件,里面规定了
我们随便看一段
{
“entry”: {
“description”: “The entry point(s) of the compilation.”,
“anyOf”: [
{
“$ref”: “#/definitions/Entry”
}
]
},
“externals”: {
“description”: “Specify dependencies that shouldn’t be resolved by webpack, but should become dependencies of the resulting bundle. The kind of the dependency depends on `output.libraryTarget`.”,
“anyOf”: [
{
“$ref”: “#/definitions/Externals”
}
]
},
“loader”: {
“description”: “Custom values available in the loader context.”,
“type”: “object”
}
}
这是对你输入 options 的规定 loader 的 type 要求是 object$ref 是 #/definitions/Externals 其实就是本 json 下的 definitions/Externals 这样写可以提取公用的配置,避免代码冗余 一共 2100 行,其中 definitions 就占了 1800 行
接下里进入 validateSchema.js 函数
// 引入 ajv
const Ajv = require(“ajv”);
const ajv = new Ajv({
errorDataPath: “configuration”,
allErrors: true,
verbose: true
});
引入了 ajv,我们在 gihub 搜索 ajv 链接我们看到,在文档里这样的描述的用法
var Ajv = require(‘ajv’);
var ajv = new Ajv(); // options can be passed, e.g. {allErrors: true}
var validate = ajv.compile(schema);
var valid = validate(data);
if (!valid) console.log(validate.errors);
这个逻辑比较简单 new 一个 ajv,调用方法 ajv.compile(schema),schema 就是你规定的对象 validate 是返回的一个函数把要验证的数据传入参数,如果有错误会记在 valid 里
const validateObject = (schema, options) => {
const validate = ajv.compile(schema);
const valid = validate(options);
return valid ? [] : filterErrors(validate.errors);
};
很显然,webpack 也是这样使用的,如果有错误,调用 filterErrors,又对错误进行了一层包装
validate 函数实在是太长了就不贴了,对 ajv 有兴趣的可以研究研究,输入什么,输出的错误会又怎样的格式输出等这个不影响主线,我们接着往下读
// 如果有错误就交给 WebpackOptionsValidationError 对象处理
if (webpackOptionsValidationErrors.length) {
throw new WebpackOptionsValidationError(webpackOptionsValidationErrors);
}
接下来

let compiler;
// 多配置
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;
new NodeEnvironmentPlugin().apply(compiler);
if (options.plugins && Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
if (typeof plugin === “function”) {
plugin.call(compiler, compiler);
} else {
plugin.apply(compiler);
}
}
}
compiler.hooks.environment.call();
compiler.hooks.afterEnvironment.call();
compiler.options = new WebpackOptionsApply().process(options, compiler);
} else {
throw new Error(“Invalid argument: options”);
}
options = new WebpackOptionsDefaulter().process(options);`

我们走单配置即传值为对象的时候,传进去一个 options 输入一个 options 很明显是是给 options 添加默认配置就是给 options 多挂在几个默认属性,至于怎么添加的,添加了什么,不是重点,感兴趣的可以读读 WebpackOptionsDefaulter 函数大部分的复杂架构,无论是 vue 和 react, 都会对你传入的对象作出相应的默认处理,接着往下
// 调用 Compile,传入当前文件路径
compiler = new Compiler(options.context);
// compiler 对象上挂载属性 options
compiler.options = options;
// compiler 对象上挂载属性 options
// 给 compiler 加载 node 环境
new NodeEnvironmentPlugin().apply(compiler);
//options.plugins 存在且为数组
if (options.plugins && Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
if (typeof plugin === “function”) {
plugin.call(compiler, compiler);
} else {
//plugin 是对象怎么会有改变 this 的 apply 方法?后续代查
plugin.apply(compiler);
}
}
}
compiler.hooks.environment.call();
compiler.hooks.afterEnvironment.call();
compiler.options = new WebpackOptionsApply().process(options, compiler);
接下来,我们看 Compiler 模块

退出移动版