放个流程图:

  这里也放一下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-endsWith
if (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 request
callback(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 request
callback(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 => browser
var 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 request
callback(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 alternatives
callback(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),
// undefined
relativePath: 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的配置对象内容

.31-浅析webpack源码之doResolve事件流(2)的更多相关文章

  1. .30-浅析webpack源码之doResolve事件流(1)

    这里所有的插件都对应着一个小功能,画个图整理下目前流程: 上节是从ParsePlugin中出来,对'./input.js'入口文件的路径做了处理,返回如下: ParsePlugin.prototype ...

  2. .30-浅析webpack源码之doResolve事件流(2)

    这里所有的插件都对应着一个小功能,画个图整理下目前流程: 上节是从ParsePlugin中出来,对'./input.js'入口文件的路径做了处理,返回如下: ParsePlugin.prototype ...

  3. .29-浅析webpack源码之doResolve事件流(1)

    在上一节中,最后返回了一个resolver,本质上就是一个Resolver对象: resolver = new Resolver(fileSystem); 这个对象的构造函数非常简单,只是简单的继承了 ...

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

    流程图如下: 重回DescriptionFilePlugin 上一节最后进入relative事件流,注入地点如下: // relative plugins.push(new DescriptionFi ...

  5. .33-浅析webpack源码之doResolve事件流(5)

    file => FileExistsPlugin 这个事件流快接近尾声了,接下来是FileExistsPlugin,很奇怪的是在最后才来检验路径文件是否存在. 源码如下: FileExistsP ...

  6. .34-浅析webpack源码之事件流make(3)

    新年好呀~过个年光打游戏,function都写不顺溜了. 上一节的代码到这里了: // NormalModuleFactory的resolver事件流 this.plugin("resolv ...

  7. 浅析libuv源码-node事件轮询解析(3)

    好像博客有观众,那每一篇都画个图吧! 本节简图如下. 上一篇其实啥也没讲,不过node本身就是这么复杂,走流程就要走全套.就像曾经看webpack源码,读了300行代码最后就为了取package.js ...

  8. .27-浅析webpack源码之事件流make(2)

    上一节跑到了NormalModuleFactory模块,调用了原型方法create后,依次触发了before-rsolve.factory.resolver事件流,这节从resolver事件流开始讲. ...

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

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

随机推荐

  1. shell 踩坑记

    变量赋值时,等号两边不能有空格: 在判断表达式中,不论是 [ -n "$1" ] 还是 [ -f  "$1" ] 都要在变量两侧加上双引号: 在使用与或非判断式 ...

  2. 响应式布局—设备像素密度测试 (-webkit-min-device-pixel-ratio)

      最近遇到这种头疼的问题,百思不得其解,不耻下问,悬梁刺股这些事情都做过之后,终于看到希望,于是攒见好就收,感觉整理分享给大家,希望有所帮助. 对手机分辨率和网页像素的初步认识是,是2倍的差别. 但 ...

  3. ADO.NET访问数据库

    1:ADO.NET数据库的方法和技术 2:ADO.NET的主要组成: 1>DataSet(数据集)-----独立于数据间的数据访问 2>.NETFramework(数据提供程序)----- ...

  4. 深入剖析MSAA

    本文打算对MSAA(Multisample anti aliasing)做一个深入的讲解,包括基本的原理.以及不同平台上的实现对比(主要是PC与Mobile).为了对MSAA有个更好的理解,所以写下了 ...

  5. SourceTree管理工具的一些使用总结

    一.冲突解决 在团队合作中,如果两个人同时修改一个文件 ,这个时候如果合并他人提交的代码是会产生冲突的,怎么解决? 1.先将代码提交至本地服务器 2.合并他人代码,这个时候在工作副本中会显示我们冲突的 ...

  6. TurnipBit开发板“趣味赛”:平衡力大比拼

    让孩子在快乐自由的游戏中培养编程思维 平衡力大挑战是我们经常经常玩的的一个小游戏,脑补画面的话比较常见的是单腿平衡力大比拼,摇晃幅度小者胜利.游戏好玩归好玩,但是想要公平判断胜负却不容易.下面就教大家 ...

  7. jsp基础了解

    1.什么是动态页面:    所谓的动态网页,是指跟静态网页相对的一种网页编程技术.静态网页,随着html代码的生成,页面的内容和显示效果就基本上不会发生变化了--除非你修改页面代码.而动态网页则不然, ...

  8. 在ASP.NET Core 2.0中使用CookieAuthentication

    在ASP.NET Core中关于Security有两个容易混淆的概念一个是Authentication(认证),一个是Authorization(授权).而前者是确定用户是谁的过程,后者是围绕着他们允 ...

  9. UML 类图基础

    先留个坑, 后续再填 UML 类图中主要包括以下几种关系: 1. 泛化: 定义:继承关系,指定子类继承父类的所有特征与行为. 图形:三角形箭头,指向父类.         2. 实现 定义:类与接口的 ...

  10. vue的组件小操作

    项目技术: webpack + vue + element + axois (vue-resource) + less-loader+ ... vue的操作的方法案例: 1.数组数据还未获取到,做出预 ...