.36-浅析webpack源码之Parser类
2018-06-24 01:33:57来源:未知 阅读 ()
眼看webpack4都出了,我还在撸3的源码,真的是捉急啊……
不过现在只是beta版本,等出稳定版本后跑跑4的源码去。
之前漏了一个东西没有讲,如下:
asyncLib.parallel([/**/], (err, results) => { if (err) return callback(err); loaders = results[0].concat(loaders, results[1], results[2]); process.nextTick(() => { callback(null, { context: context, request: loaders.map(loaderToIdent).concat([resource]).join("!"), dependencies: data.dependencies, userRequest, rawRequest: request, loaders, resource, resourceResolveData, // 这个东西 parser: this.getParser(settings.parser) }); }); });
在解析完入口文件路径与JS文件对应的babel-loader路径后,返回的包装对象多了一个parser属性,看似简单,实则麻烦的要死。
这里的参数settings.parser为undefined,来源如下:
const settings = {}; const useLoadersPost = []; const useLoaders = []; const useLoadersPre = []; result.forEach(r => { if (r.type === "use") { // ... } else { // parser参数 settings[r.type] = r.value; } });
这里的参数来源于modules.rules的parser参数,一般情况下不会传吧,反正vue-cli中没有……
因此这里的parserOptions参数为undefined,直接看原型方法getParser:
getParser(parserOptions) { // 默认键 let ident = "null"; // 检测参数 if (parserOptions) { if (parserOptions.ident) ident = parserOptions.ident; else ident = JSON.stringify(parserOptions); } // 尝试获取缓存 const parser = this.parserCache[ident]; if (parser) return parser; // 创建新的parser并设置缓存 return this.parserCache[ident] = this.createParser(parserOptions); }
老套的尝试获取对应键的缓存值,如果不存在就新建一个,所有参数为undefined的默认键为null字符串。
createParser(parserOptions) { // new一个Parser对象 const parser = new Parser(); // 触发事件流 this.applyPlugins2("parser", parser, parserOptions || {}); // 返回parser return parser; }
这个方法简单易懂,就是生成一个新的parser,但是每一行代码都是分量足足,让我联想到了vue源码中的parse方法……
Parser类
这东西就跟compiler、compilation对象一样,内容非常繁杂,干讲是没办法的,所以简单过一下构造,调用的时候跑内部方法。
class Parser extends Tapable { constructor(options) { super(); this.options = options; this.scope = undefined; this.state = undefined; this.comments = undefined; this.initializeEvaluating(); } initializeEvaluating() { this.plugin("evaluate Literal", expr => { switch (typeof expr.value) { case "number": return new BasicEvaluatedExpression().setNumber(expr.value).setRange(expr.range); case "string": return new BasicEvaluatedExpression().setString(expr.value).setRange(expr.range); case "boolean": return new BasicEvaluatedExpression().setBoolean(expr.value).setRange(expr.range); } if (expr.value === null) return new BasicEvaluatedExpression().setNull().setRange(expr.range); if (expr.value instanceof RegExp) return new BasicEvaluatedExpression().setRegExp(expr.value).setRange(expr.range); }); // 非常多的plugin... // 非常非常多的原型方法 } }
这个类内容非常多,但是内容类型很简单,只有事件流的注入与一些原型方法,所以在初始化不会有任何实际动作。
这样就返回了一个包含大量事件流的Parser对象,之后apply的时候再回头解析。
parser事件流
这个事件流回头翻了好几节才找到,集中在第24节的大图中,插件列表如下:
compiler.apply( new CompatibilityPlugin(), new HarmonyModulesPlugin(options.module), new AMDPlugin(options.module, options.amd || {}), new CommonJsPlugin(options.module), new LoaderPlugin(), new NodeStuffPlugin(options.node), new RequireJsStuffPlugin(), new APIPlugin(), new ConstPlugin(), new UseStrictPlugin(), new RequireIncludePlugin(), new RequireEnsurePlugin(), new RequireContextPlugin(options.resolve.modules, options.resolve.extensions, options.resolve.mainFiles), new ImportPlugin(options.module), new SystemPlugin(options.module) );
NodeSourcePlugin
但是第一个事件是在NodeSourcePlugin插件中,注入地点如下:
class WebpackOptionsApply extends OptionsApply { constructor() { super(); } process(options, compiler) { // ... 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; // ... } // ... } // ... } }
感觉一下回到了2017年的感觉……
很遗憾,这个插件很快就挂了,源码简要如下:
// NodeSourcePlugin.js params.normalModuleFactory.plugin("parser", function(parser, parserOptions) { if (parserOptions.node === false) return; // ...more });
由于传进来的parserOptions为空对象,所以直接返回。
剩下的均来源于后面的批量plugin。
CompatibilityPlugin、HarmonyModulesPlugin...
// CompatibilityPlugin.js params.normalModuleFactory.plugin("parser", (parser, parserOptions) => { if (typeof parserOptions.browserify !== "undefined" && !parserOptions.browserify) return; // ... });
阵亡。
后面的HarmonyModulesPlugin、AMDPlugin、CommonJsPlugin、RequireJsStuffPlugin分别尝试获取parserOptions的对应属性,失败直接返回。
APIPlugin
这个没有阵亡,做了点事。
const REPLACEMENTS = { __webpack_require__: "__webpack_require__", // eslint-disable-line camelcase __webpack_public_path__: "__webpack_require__.p", // eslint-disable-line camelcase __webpack_modules__: "__webpack_require__.m", // eslint-disable-line camelcase __webpack_chunk_load__: "__webpack_require__.e", // eslint-disable-line camelcase __non_webpack_require__: "require", // eslint-disable-line camelcase __webpack_nonce__: "__webpack_require__.nc", // eslint-disable-line camelcase "require.onError": "__webpack_require__.oe" // eslint-disable-line camelcase }; const REPLACEMENT_TYPES = { __webpack_public_path__: "string", // eslint-disable-line camelcase __webpack_require__: "function", // eslint-disable-line camelcase __webpack_modules__: "object", // eslint-disable-line camelcase __webpack_chunk_load__: "function", // eslint-disable-line camelcase __webpack_nonce__: "string" // eslint-disable-line camelcase }; class APIPlugin { apply(compiler) { compiler.plugin("compilation", (compilation, params) => { // set... params.normalModuleFactory.plugin("parser", parser => { // 简单明了注入事件流 Object.keys(REPLACEMENTS).forEach(key => { // 'expression __webpack_require__'等等 parser.plugin(`expression ${key}`, ParserHelpers.toConstantDependency(REPLACEMENTS[key])); // 'evaluate typeof __webpack_require__'等等 parser.plugin(`evaluate typeof ${key}`, ParserHelpers.evaluateToString(REPLACEMENT_TYPES[key])); }); }); }); } } module.exports = APIPlugin;
十分的简单,根据两个内置的配置对象批量注入事件,通过ParserHelpers类来辅助生成事件函数。
该辅助类的两个方法源码如下:
ParserHelpers.toConstantDependency = function(value) { // value来源于APIPlugin插件 // expr为触发事件流时给的参数 暂时不知道是什么 return function constDependency(expr) { var dep = new ConstDependency(value, expr.range); dep.loc = expr.loc; this.state.current.addDependency(dep); return true; }; };
ParserHelpers.evaluateToString = function(value) { // 一样的 return function stringExpression(expr) { return new BasicEvaluatedExpression().setString(value).setRange(expr.range); }; };
这里又引入了两个辅助类来生成新对象。
第一个方法有一个方法调用:this.state.current.addDependency,我是根本找不到定义的地点,不知道这个方法是如何执行的,尝试在控制台输出,结果发现整个打包过程中,根本就没有触发这两批事件流。
这就完全无法得知其作用了,所以这个插件暂时跳过,可以当做没有。
ConstPlugin
这个插件注入了三个事件流,简单看一下:
params.normalModuleFactory.plugin("parser", parser => { parser.plugin("statement if", function(statement) { // ... }); parser.plugin("expression ?:", function(expression) { // ... }); parser.plugin("evaluate Identifier __resourceQuery", function(expr) { // ... }); parser.plugin("expression __resourceQuery", function() { // ... }); });
UseStrictPlugin
// UseStrictPlugin.js params.normalModuleFactory.plugin("parser", (parser) => { const parserInstance = parser; parser.plugin("program", (ast) => { // ... }); });
注入program事件流。
后面的RequireIncludePlugin、RequireEnsurePlugin、RequireContextPlugin、ImportPlugin、SystemPlugin插件全部跳过,内容我就不贴进来了。
总的来说,由于传进来的options为空对象,这个parser事件流的触发毫无意义,只是单纯注入了几个事件流,最后返回了这个parser对象,作为了request的一个对象参数,具体原型方法的调用后面再看。
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:AJAX跨域问题解决思路
- 浅析XMLHttpRequest的缓存问题 2020-02-25
- webpack打包配置禁止html标签全部转为小写 2019-08-14
- 入门webpack,看这篇就够了 2019-08-14
- image-webpack-loader包安装报错解决 2019-08-14
- Vue学习之webpack中使用vue(十七) 2019-08-14
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash