赶紧完结这个系列咯,webpack4都已经出正式版了。

  之前的代码搜索到js文件的对应loader,并添加到了对象中返回,流程如下:

this.plugin("factory", () => (result, callback) => {
let resolver = this.applyPluginsWaterfall0("resolver", null); // Ignored
if (!resolver) return callback(); // 这里的data就是返回的大对象
/*
data =>
{
context: 'd:\\workspace\\doc',
request: 'd:\\workspace\\node_modules\\babel-loader\\lib\\index.js!d:\\workspace\\doc\\input.js',
dependencies: [SingleEntryDependency],,
userRequest: 'd:\\workspace\\doc\\input.js',
rawRequest: './input.js',
loaders: [ { loader: 'd:\\workspace\\node_modules\\babel-loader\\lib\\index.js' } ],
resource: 'd:\\workspace\\doc\\input.js',
还有package.json与parser相关的属性
}
*/
resolver(result, (err, data) => {
if (err) return callback(err); // Ignored
if (!data) return callback(); // direct module
if (typeof data.source === "function")
return callback(null, data); this.applyPluginsAsyncWaterfall("after-resolve", data, (err, result) => {
// ...
})
})
})

  这个对象的request将入口文件的路径与loader拼接起来并用!分割,所有的属性基本上都与路径相关。

after-resolve事件流

  这里会触发after-resolve事件流,注入地点如下:

const matchJson = /\.json$/i;
params.normalModuleFactory.plugin("after-resolve", (data, done) => {
// if this is a json file and there are no loaders active, we use the json-loader in order to avoid parse errors
// @see https://github.com/webpack/webpack/issues/3363
if (matchJson.test(data.request) && data.loaders.length === 0) {
data.loaders.push({
loader: jsonLoaderPath
});
}
done(null, data);
});

  注释已经写的很明白了,这里是检测待处理文件类型是否是json文件,如果是并且没有对应的loader,就将内置的json-loader作为loader。

callback

  接下来看回调函数内容:

this.applyPluginsAsyncWaterfall("after-resolve", data, (err, result) => {
if (err) return callback(err); // Ignored
if (!result) return callback();
// 无此事件流 返回undefined
let createdModule = this.applyPluginsBailResult("create-module", result);
if (!createdModule) { if (!result.request) {
return callback(new Error("Empty dependency (no request)"));
}
// 创建
createdModule = new NormalModule(
result.request,
result.userRequest,
result.rawRequest,
result.loaders,
result.resource,
result.parser
);
}
// 无此事件流
createdModule = this.applyPluginsWaterfall0("module", createdModule); return callback(null, createdModule);
});

  这里的两个事件流都是没有的,所以只需要看那个创建类的过程,然后就直接返回了。

  只看下构造函数:

class NormalModule extends Module {
/*
request => 'd:\\workspace\\node_modules\\babel-loader\\lib\\index.js!d:\\workspace\\doc\\input.js'
userRequest => 'd:\\workspace\\doc\\input.js'
rawRequest => './input.js'
loaders => [ { loader: 'd:\\workspace\\node_modules\\babel-loader\\lib\\index.js' } ]
resource => 'd:\\workspace\\doc\\input.js'
parser => [Parser]
*/
constructor(request, userRequest, rawRequest, loaders, resource, parser) {
super();
this.request = request;
this.userRequest = userRequest;
this.rawRequest = rawRequest;
this.parser = parser;
this.resource = resource;
this.context = getContext(resource);
this.loaders = loaders;
this.fileDependencies = [];
this.contextDependencies = [];
this.warnings = [];
this.errors = [];
this.error = null;
this._source = null;
this.assets = {};
this.built = false;
this._cachedSource = null;
} // ...原型方法
}

  只有一个getContext方法是执行的,这个方法比较简单,过一下就行:

exports.getContext = function getContext(resource) {
var splitted = splitQuery(resource);
return dirname(splitted[0]);
}; // 切割参数
function splitQuery(req) {
var i = req.indexOf("?");
if (i < 0) return [req, ""];
return [req.substr(0, i), req.substr(i)];
} // 返回目录
// d:\\workspace\\doc\\input.js => d:\\workspace\\doc(反斜杠这里有转义)
function dirname(path) {
if (path === "/") return "/";
var i = path.lastIndexOf("/");
var j = path.lastIndexOf("\\");
var i2 = path.indexOf("/");
var j2 = path.indexOf("\\");
var idx = i > j ? i : j;
var idx2 = i > j ? i2 : j2;
if (idx < 0) return path;
if (idx === idx2) return path.substr(0, idx + 1);
return path.substr(0, idx);
}

  返回了入口文件路径的目录,也就是上下文。

  这样一路回调返回,回到了最初的原型方法create:

// NormalModuleFactory
const factory = this.applyPluginsWaterfall0("factory", null); // Ignored
if (!factory) return callback(); factory(result, (err, module) => {
// 这里的module就是new出来的NormalModule对象
if (err) return callback(err);
// this.cachePredicate = typeof options.unsafeCache === "function" ? options.unsafeCache : Boolean.bind(null, options.unsafeCache);
// 由于默认情况下unsafeCache为true 所以这个函数默认一直返回true
if (module && this.cachePredicate(module)) {
dependencies.forEach(d => d.__NormalModuleFactoryCache = module);
} callback(null, module);
});

  这里对之前处理的入口文件对象做了缓存。

  返回又返回,回到了Compilation对象中:

_addModuleChain(context, dependency, onModule, callback) {
// ... this.semaphore.acquire(() => {
// 从这里出来
moduleFactory.create({
contextInfo: {
issuer: "",
compiler: this.compiler.name
},
context: context,
dependencies: [dependency]
}, (err, module) => {
if (err) {
this.semaphore.release();
return errorAndCallback(new EntryModuleNotFoundError(err));
} let afterFactory;
// 不存在的属性
if (this.profile) {
if (!module.profile) {
module.profile = {};
}
afterFactory = Date.now();
module.profile.factory = afterFactory - start;
} const result = this.addModule(module);
// ...
});
});
}

  常规的错误处理,然后判断了下profile属性,这个属性大概是用计算前面factory整个事件流的触发时间。

  总之,接着调用了addModule原型方法

addModule

// 第二个参数未传
addModule(module, cacheGroup) {
// 这里调用的是原型方法
/*
identifier() {
return this.request;
}
*/
// 愚蠢的方法
const identifier = module.identifier();
// 空对象没有的
if (this._modules[identifier]) {
return false;
}
// 缓存名是'm' + request
const cacheName = (cacheGroup || "m") + identifier;
// 如果有缓存的情况下
if (this.cache && this.cache[cacheName]) {
// ...
}
// 这个方法是在父类上面
// 作用是清空属性
// 然而并没有什么属性可以清 跳过
module.unbuild();
// 将类分别加入各个对象/数组
this._modules[identifier] = module;
if (this.cache) {
this.cache[cacheName] = module;
}
this.modules.push(module);
return true;
}

  这个方法其实并没有做什么实际的东西,简单来讲只是添加了缓存。

  接下来看下面的代码:

_addModuleChain(context, dependency, onModule, callback) {
// ... this.semaphore.acquire(() => {
moduleFactory.create({
contextInfo: {
issuer: "",
compiler: this.compiler.name
},
context: context,
dependencies: [dependency]
}, (err, module) => {
// ...
// 返回一个true
const result = this.addModule(module);
// 跳过下面两步
if (!result) {
// ...
} if (result instanceof Module) {
// ...
}
// 进入这里
/*
(module) => {
entry.module = module;
this.entries.push(module);
module.issuer = null;
}
*/
onModule(module); this.buildModule(module, false, null, null, (err) => {
if (err) {
this.semaphore.release();
return errorAndCallback(err);
} if (this.profile) {
const afterBuilding = Date.now();
module.profile.building = afterBuilding - afterFactory;
} moduleReady.call(this);
}); function moduleReady() {
this.semaphore.release();
this.processModuleDependencies(module, err => {
if (err) {
return callback(err);
} return callback(null, module);
});
}
});
});
}

  这个onModule来源于方法的第三个参数,比较简单,没做什么事,然后继续跑调用了原型方法buildModule。

buildModule

// this.buildModule(module, false, null, null, (err) => {})
buildModule(module, optional, origin, dependencies, thisCallback) {
// 无此事件流
this.applyPlugins1("build-module", module);
if (module.building) return module.building.push(thisCallback);
const building = module.building = [thisCallback]; function callback(err) {
module.building = undefined;
building.forEach(cb => cb(err));
}
module.build(this.options, this, this.resolvers.normal, this.inputFileSystem, (error) => {
// ...
});
}

  总的来说并没有做什么事,传进来的callback被封装并带入build方法的回调中。

module.build

// options为添加了默认参数的配置对象webpack.config.js
build(options, compilation, resolver, fs, callback) {
this.buildTimestamp = Date.now();
this.built = true;
this._source = null;
this.error = null;
this.errors.length = 0;
this.warnings.length = 0;
this.meta = {}; return this.doBuild(options, compilation, resolver, fs, (err) => {
// ...
});
}

  一串串的赋值,又是doBuild方法。

module.doBuild

doBuild(options, compilation, resolver, fs, callback) {
this.cacheable = false;
const loaderContext = this.createLoaderContext(resolver, options, compilation, fs); runLoaders({
resource: this.resource,
loaders: this.loaders,
context: loaderContext,
readResource: fs.readFile.bind(fs)
}, (err, result) => {
// ...
});
}

  呃,已经连续调用好多个函数了。

createLoaderContext

createLoaderContext(resolver, options, compilation, fs) {
const loaderContext = {
// ..
};
compilation.applyPlugins("normal-module-loader", loaderContext, this);
// 无此值
if (options.loader)
Object.assign(loaderContext, options.loader); return loaderContext;
}

  这里先不关心这个loaderContext里面到底有什么,因为后面会调用的,直接看事件流'normal-module-loader'。

// LoaderTargetPlugin.js
compilation.plugin("normal-module-loader", (loaderContext) => loaderContext.target = this.target);

  这里由于配置对象没有target参数,所以这个没有用。

// LoaderPlugin.js
compilation.plugin("normal-module-loader", (loaderContext, module) => {
loaderContext.loadModule = function loadModule(request, callback) {
// ...
};
});

  这个仅仅是加了个属性方法,跳过。

  最后返回了一个包含很多方法的对象loaderContext。

  第二步是调用runLoaders方法,将之前生成的对象传进去,这里终于要调用loader对文件进行处理了!!!!

  下节可以有干货,这节流水账先这样了。

最新文章

  1. oracle中的查询语句(关于出库入库信息表,明细表,把捆包箱表,单位信息表的集中查询)
  2. memcached完全剖析–1. memcached的基础
  3. Android 时间格式的正则表达式
  4. Linux学习之chage命令
  5. C++对象模型2--指针cout结果
  6. 3400: [Usaco2009 Mar]Cow Frisbee Team 奶牛沙盘队
  7. C# 跨进程 设置窗口owner
  8. LODOP设置打印设计返回JS代码是变量
  9. Leetcode_5.最长回文子串
  10. Android 常用RGB值以及中英文名称
  11. 在 Windows 10 中使用 OpenAI Spinning Up
  12. 判断一个url是否是图片
  13. C#内存释放(垃圾回收)
  14. java基础----&gt;java中变参函数的使用
  15. poj3107 Godfather 求树的重心
  16. Linux下C语言编程中库的使用
  17. 深入了解SQL Tuning Advisor(转载)
  18. kill me heal me的链接
  19. Oracle中-事务-序列-视图-数据类型笔记
  20. 【VBA编程】13.Workbook对象的事件

热门文章

  1. ubuntu下Node.js环境搭建
  2. 如何把jar包发布到maven私服
  3. SharePoint 列表中增加列编辑功能菜单
  4. StringBuilder 详解 (String系列之2)
  5. PowerDesigner执行脚本 name/comment/stereotype互转
  6. python中硬要写抽象类和抽象方法
  7. [学习笔记]后缀自动机SAM
  8. 关于 IPv6
  9. raspberry pi wifi
  10. vue项目常见需求(项目实战笔记)