放个流程图:

  这里也放一下request对象内容,这节完事后如下(把vue-cli的package.json也复制过来了):

/*{ context: { issuer: '', compiler: undefined },path: 'd:\\workspace\\doc',request: './input.js',query: '',module: false,directory: false,file: false,descriptionFilePath: 'd:\\workspace\\doc\\package.json',descriptionFileData: *package.json内容*,descriptionFileRoot: 'd:\\workspace\\doc',relativePath: '.',__innerRequest_request: './input.js',__innerRequest_relativePath: '.',__innerRequest: './input.js' }
*/

  上一节看到这:

// 调用的是callback()
function innerCallback(err, result) {if (arguments.length > 0) {if (err) return callback(err);if (result) return callback(null, result);return callback();}runAfter();
}

  这里接下来会调用runAfter方法,之前有讲解过这个,简单讲就是触发after-type的事件流,这里的type为parsed-resolve,即触发after-parsed-resolve事件流。

  来源如下:

plugins.push(new NextPlugin("after-parsed-resolve", "described-resolve"));

  这个插件就非常简单了:

NextPlugin.prototype.apply = function(resolver) {var target = this.target;resolver.plugin(this.source, function(request, callback) {resolver.doResolve(target, request, null, callback);});
};

  直接调用doResolve方法触发下一个target的事件流,比起有描述文件的情况,这里的区别就是request少了几个参数,触发下一个事件流时没有message。

  刚发现事件流的名字代表着某阶段,此时代表描述文件解析完毕。

  接下来的事件流来源于以下几个插件:

// described-resolve
alias.forEach(function(item) {plugins.push(new AliasPlugin("described-resolve", item, "resolve"));
});
plugins.push(new ConcordModulesPlugin("described-resolve", {}, "resolve"));
aliasFields.forEach(function(item) {plugins.push(new AliasFieldPlugin("described-resolve", item, "resolve"));
});
plugins.push(new ModuleKindPlugin("after-described-resolve", "raw-module"));
plugins.push(new JoinRequestPlugin("after-described-resolve", "relative"));

AliasPlugin  

  先从第一个开始看,alias参数引用vue-cli的代码,这里的alias在上面的第二部分进行了转换(具体可参考28节)。

  数组的元素作为参数传入了AliasPlugin插件中,源码如下:

/*
源配置alias: {'vue$': 'vue/dist/vue.esm.js','@': '../src'}
转换后为alias:[{name: 'vue',onlyModule: true,alias: 'vue/dist/vue.esm.js'},{name: '@',onlyModule: false,alias: '../src'           }]
*/
AliasPlugin.prototype.apply = function(resolver) {var target = this.target;var name = this.name;var alias = this.alias;var onlyModule = this.onlyModule;resolver.plugin(this.source, function(request, callback) {var innerRequest = request.request;if (!innerRequest) return callback();// 两个元素传进来并不满足if条件 跳过// startsWith可参考ES6的新方法http://es6.ruanyifeng.com/#docs/string#includes-startsWith-endsWithif (innerRequest === name || (!onlyModule && startsWith(innerRequest, name + "/"))) {if (innerRequest !== alias && !startsWith(innerRequest, alias + "/")) {var newRequestStr = alias + innerRequest.substr(name.length);var obj = Object.assign({}, request, {request: newRequestStr});return resolver.doResolve(target, obj, "aliased with mapping '" + name + "': '" + alias + "' to '" + newRequestStr + "'", createInnerCallback(function(err, result) {if (arguments.length > 0) return callback(err, result);// don't allow other aliasing or raw requestcallback(null, null);}, callback));}}return callback();});
};

  不太懂这里的处理是干什么,反正两个元素传进来都没有满足if条件,跳过。

ConcordModulesPlugin

  described-resolve事件流还没有完,所以callback执行后只是记数,下一个插件源码如下:

ConcordModulesPlugin.prototype.apply = function(resolver) {var target = this.target;resolver.plugin(this.source, function(request, callback) {// 获取的还是'./input.js'var innerRequest = getInnerRequest(resolver, request);if (!innerRequest) return callback();// request.descriptionFileData就是配置文件package.json中的内容var concordField = DescriptionFileUtils.getField(request.descriptionFileData, "concord");// 找不到该属性直接返回if (!concordField) return callback();// 下面的都不用跑了var data = concord.matchModule(request.context, concordField, innerRequest);if (data === innerRequest) return callback();if (data === undefined) return callback();if (data === false) {var ignoreObj = Object.assign({}, request, {path: false});return callback(null, ignoreObj);}var obj = Object.assign({}, request, {path: request.descriptionFileRoot,request: data});resolver.doResolve(target, obj, "aliased from description file " + request.descriptionFilePath + " with mapping '" + innerRequest + "' to '" + data + "'", createInnerCallback(function(err, result) {if (arguments.length > 0) return callback(err, result);// Don't allow other aliasing or raw requestcallback(null, null);}, callback));});
};

  这里有两个工具方法:getInnerRequest、getFiled,第一个获取request的inner属性,代码如下:

module.exports = function getInnerRequest(resolver, request) {// 第一次进来是没有这些属性的if (typeof request.__innerRequest === "string" &&request.__innerRequest_request === request.request &&request.__innerRequest_relativePath === request.relativePath)return request.__innerRequest;var innerRequest;// './input.js'if (request.request) {innerRequest = request.request;// 尝试获取relativePath属性进行拼接if (/^\.\.?\//.test(innerRequest) && request.relativePath) {innerRequest = resolver.join(request.relativePath, innerRequest);}} else {innerRequest = request.relativePath;}// 属性添加request.__innerRequest_request = request.request;request.__innerRequest_relativePath = request.relativePath;return request.__innerRequest = innerRequest;
};

  总的来说就是尝试获取__innerRequest属性,但是初次进来是没有的,所以会在后面进行添加,最后返回的仍然是'./input.js'。

  第二个方法就比较简单了,只是从之前读取的package.json对象查询对应的字段,代码如下:

// content为package.json配置对象
function getField(content, field) {if (!content) return undefined;// 数组及单key模式if (Array.isArray(field)) {var current = content;for (var j = 0; j < field.length; j++) {if (current === null || typeof current !== "object") {current = null;break;}current = current[field[j]];}if (typeof current === "object") {return current;}} else {if (typeof content[field] === "object") {return content[field];}}
}

  代码非常简单,这里就不讲了。

  常规情况下,没人会去设置concord属性吧,在vue-cli我也没看到,这里先跳过。

AliasFieldPlugin

  接下来是这个不知道干啥的插件,处理的是resolve.aliasFields参数,默认参数及插件源码如下:

// "aliasFields": ["browser"],
AliasFieldPlugin.prototype.apply = function(resolver) {var target = this.target;var field = this.field;resolver.plugin(this.source, function(request, callback) {if (!request.descriptionFileData) return callback();// 一样的var innerRequest = getInnerRequest(resolver, request);if (!innerRequest) return callback();// filed => browservar fieldData = DescriptionFileUtils.getField(request.descriptionFileData, field);if (typeof fieldData !== "object") {if (callback.log) callback.log("Field '" + field + "' doesn't contain a valid alias configuration");return callback();}var data1 = fieldData[innerRequest];var data2 = fieldData[innerRequest.replace(/^\.\//, "")];var data = typeof data1 !== "undefined" ? data1 : data2;if (data === innerRequest) return callback();if (data === undefined) return callback();if (data === false) {var ignoreObj = Object.assign({}, request, {path: false});return callback(null, ignoreObj);}var obj = Object.assign({}, request, {path: request.descriptionFileRoot,request: data});resolver.doResolve(target, obj, "aliased from description file " + request.descriptionFilePath + " with mapping '" + innerRequest + "' to '" + data + "'", createInnerCallback(function(err, result) {if (arguments.length > 0) return callback(err, result);// Don't allow other aliasing or raw requestcallback(null, null);}, callback));});
};

  开头跟之前那个是一样的,也是调用getField从package.json中获取对应的配置,但是这个默认的browser我在vue-cli也找不到,暂时跳过。

  正常处理完described-resolve事件流,继续执行runafter触发after-described-resolve事件流,来源如下:

plugins.push(new ModuleKindPlugin("after-described-resolve", "raw-module"));
plugins.push(new JoinRequestPlugin("after-described-resolve", "relative"));

ModuleKindPlugin

ModuleKindPlugin.prototype.apply = function(resolver) {var target = this.target;resolver.plugin(this.source, function(request, callback) {// 判断module属性if (!request.module) return callback();var obj = Object.assign({}, request);// 删除module属性delete obj.module;// 直接触发下一个事件流resolver.doResolve(target, obj, "resolve as module", createInnerCallback(function(err, result) {if (arguments.length > 0) return callback(err, result);// Don't allow other alternativescallback(null, null);}, callback));});
};

  这里的处理十分简单,判断request对象是否是module,是则直接触发下一个事件流。

  而在第一次时进来的是入口文件,module属性为false,所以这里会跳过,后面处理module再回来讲。

JoinRequestPlugin

JoinRequestPlugin.prototype.apply = function(resolver) {var target = this.target;resolver.plugin(this.source, function(request, callback) {var obj = Object.assign({}, request, {// request.path => d:\\workspace\\doc// request.request => ./input.js// 在join方法中会被拼接成d:\workspace\doc\.\input.js// 最后格式化返回d:\workspace\doc\input.js
            path: resolver.join(request.path, request.request),// undefinedrelativePath: request.relativePath && resolver.join(request.relativePath, request.request),request: undefined});resolver.doResolve(target, obj, null, callback);});
};

  这个地方终于把入口文件的路径拼起来了,接下来调用下一个事件流,这节先到这里。

  写完这节,总算对Resolver对象有所了解,总结如下:

1、该对象可以处理resolve参数、loader、module等等

2、插件的链式调用类似于if/else,比如说如果传进来的是一个module,插件会流向module事件流;如果是普通的文件,会流向本节所讲的方,每一种情况都有自己的结局。

3、一部分参数处理依赖于package.json的配置对象内容

转载于:https://www.cnblogs.com/QH-Jimmy/p/8340639.html

.31-浅析webpack源码之doResolve事件流(3)相关推荐

  1. .17-浅析webpack源码之compile流程-入口函数run

    本节流程如图: 现在正式进入打包流程,起步方法为run: Compiler.prototype.run = (callback) => {const startTime = Date.now() ...

  2. webpack 源码分析(四)——complier模块

    webpack 源码分析(四)--complier模块 上一篇我们看到,webpack-cli 通过 yargs 对命令行传入的参数和配置文件里的配置项做了转换包装,然后传递给 webpack 的 c ...

  3. webpack 源码分析系列 ——loader

    想要更好的格式阅读体验,请查看原文:webpack 源码分析系列 --loader 为什么需要 loader webpack是一个用于现代 JavaScript 应用程序的静态模块打包工具.内部通过构 ...

  4. webpack源码阅读——npm脚本运行webpack与命令行输入webpack的区别

    原文地址:webpack源码阅读--npm脚本执行webpack与命令行输入webpack执行的区别 如有错误,欢迎指正! webpack是目前被大家广为使用的模块打包器.从命令行输入webpack或 ...

  5. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  6. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  7. Spring5源码 - 13 Spring事件监听机制_@EventListener源码解析

    文章目录 Pre 概览 开天辟地的时候初始化的处理器 @EventListener EventListenerMethodProcessor afterSingletonsInstantiated 小 ...

  8. Spring5源码 - 12 Spring事件监听机制_异步事件监听应用及源码解析

    文章目录 Pre 实现原理 应用 配置类 Event事件 事件监听 EventListener 发布事件 publishEvent 源码解析 (反推) Spring默认的事件广播器 SimpleApp ...

  9. Spring5源码 - 11 Spring事件监听机制_源码篇

    文章目录 pre 事件监听机制的实现原理[观察者模式] 事件 ApplicationEvent 事件监听者 ApplicationEvent 事件发布者 ApplicationEventMultica ...

最新文章

  1. 英国《金融时报》:全力加码早期投资,红杉中国在下一盘怎样的棋?
  2. Universal Windows Platform(UWP)应用的窗口特性
  3. spring创建复杂对象的三种方式
  4. 【转载】MySQL Show命令总结
  5. 苏州大学计算机考研复试经验,苏州大学计算机考研复试经验总结.docx
  6. django简介及环境搭建
  7. C# Learning—Step By Step(9)
  8. keycode值列表
  9. 怎样在安卓中实现在锁屏状态下弹出对话框,并可以震动和铃声,就像闹钟似的?...
  10. aboutface4 1-3 读书笔记 幕布 思维导图
  11. 单片机c语言实验报告心得,关于单片机实训心得体会
  12. Android热修复Sophix
  13. idea 一直在updating index的问题
  14. 有一种友谊可以美的让人心颤——CHANDLER和JOEY 转贴 来自friends论坛
  15. php 文本域,关于使用文本域(TextArea)的一个问题
  16. 判断apk是否加固或混淆,Python + dex2jar-2.0实现方法
  17. Josh‘s Notes: SystemVerilog 验证 (Part 1 — 验证导论)
  18. 计算机对人类发展历史的影响,【信息技术对人类社会发展有什么影响?】_人类 发展史_全球新能源网...
  19. 模拟cmos集成电路(4)
  20. 解决Linux上steam更新游戏后导致版本不一致连接失败的问题

热门文章

  1. linux文件 run.man,【Linux】linux经常使用基本命令
  2. 关键词分词工具_快图制作工具 | 如何制作词云图?
  3. 影响个人计算机系统功能的因素,影响个人计算机系统功能的因素除了系统使用哪种位的微处理器外,还有CPU的时钟频率、CPU主内存容量、CPU所能提供的指令集。...
  4. android uinput 按键_android 使用uinput模拟输入设备的方法
  5. python中可选参数是什么意思_Python如何定义有可选参数的元类
  6. js实现阶乘算法的三种方法
  7. ubuntu终端命令停止_从命令行关闭Linux计算机的5种方法
  8. php连接函数config,smarty内置函数config_load用法实例
  9. 判断单链表中的元素是否递增_检测单链表中是否有环(C语言)
  10. ubuntu安装Qt开发环境