webpack 之 LoaderRunner 全方位揭秘

绝大多数人都在使用 webpack 作为构建工具。那么 loader 作为处理各种资源的工具,大家肯定也不会陌生。很多人没写过 loader,但是都对 loader 的具体怎么写,怎样执行的一无所知。那么本文就对 3.0.0 版本做一个全方位的揭秘。

loader

所谓 loader 只是一个导出为函数的 JavaScript 模块。它接收上一个 loader 产生的结果或者资源文件(resource file)作为入参。也可以用多个 loader 函数组成 loader chain。compiler 需要得到最后一个 loader 产生的处理结果。这个处理结果应该是 String 或者 Buffer(被转换为一个 string)。具体的用法,可以看 Loader 官网的描述。接下来我们从源码的角度去分析,为什么可以这样做,为什么可以实现同异步钩子,内部到底是怎么实现的。那么这就是 loader-runner 的作用所在。

入口

loader-runner 是一个独立出去的 npm 包,它的入口在 lib/LoaderRunner.js

exports.runLoaders = function runLoaders(options, callback) {
	// read options
	var resource = options.resource || ""; // loaders 处理的资源
	var loaders = options.loaders || []; // loaders 配置
	var loaderContext = options.context || {}; // 所有 loaders 共享的数据
	var readResource = options.readResource || readFile; // 文件输入系统

	//
	var splittedResource = resource && splitQuery(resource);
	var resourcePath = splittedResource ? splittedResource[0] : undefined; // 资源路径
	var resourceQuery = splittedResource ? splittedResource[1] : undefined; // 资源的 query
	var contextDirectory = resourcePath ? dirname(resourcePath) : null; // 资源的目录

	// execution state
	var requestCacheable = true; // 缓存的标识位
	var fileDependencies = []; // 文件依赖的缓存
	var contextDependencies = []; // 目录依赖的缓存

	// prepare loader objects
	loaders = loaders.map(createLoaderObject); // 处理 loaders 的若干属性
  // loaderContext 是在所有 loaders 处理资源时候共享的一份数据
  // loaderIndex 是一个指针,它控制了所有 loaders 的 pitch 与 normal 函数的执行
	loaderContext.context = contextDirectory;
	loaderContext.loaderIndex = 0;
	loaderContext.loaders = loaders;
	loaderContext.resourcePath = resourcePath;
	loaderContext.resourceQuery = resourceQuery;
	loaderContext.async = null; // 为了实现异步 loader 的闭包函数
	loaderContext.callback = null; // 为了实现同步或者异步 loader 的闭包函数
	loaderContext.cacheable = function cacheable(flag) {
		if(flag === false) {
			requestCacheable = false;
		}
	};
	loaderContext.dependency = loaderContext.addDependency = function addDependency(file) {
		fileDependencies.push(file);
	};
	loaderContext.addContextDependency = function addContextDependency(context) {
		contextDependencies.push(context);
	};
	loaderContext.getDependencies = function getDependencies() {
		return fileDependencies.slice();
  };
	loaderContext.getContextDependencies = function getContextDependencies() {
		return contextDependencies.slice();
  };
  // 清除所有缓存
	loaderContext.clearDependencies = function clearDependencies() {
		fileDependencies.length = 0;
		contextDependencies.length = 0;
		requestCacheable = true;
  };

  // 这些 getter/setter 都是为了在 loader 函数里面通过 this 求值能动态得到对应的值
	Object.defineProperty(loaderContext, "resource", {
		enumerable: true,
		get: function() {
			if(loaderContext.resourcePath === undefined)
				return undefined;
			return loaderContext.resourcePath + loaderContext.resourceQuery;
		},
		set: function(value) {
			var splittedResource = value && splitQuery(value);
			loaderContext.resourcePath = splittedResource ? splittedResource[0] : undefined;
			loaderContext.resourceQuery = splittedResource ? splittedResource[1] : undefined;
		}
	});
	Object.defineProperty(loaderContext, "request", {
		enumerable: true,
		get: function() {
			return loaderContext.loaders.map(function(o) {
				return o.request;
			}).concat(loaderContext.resource || "").join("!");
		}
	});
	Object.defineProperty(loaderContext, "remainingRequest", {
		enumerable: true,
		get: function() {
			if(loaderContext.loaderIndex >= loaderContext.loaders.length - 1 && !loaderContext.resource)
				return "";
			return loaderContext.loaders.slice(loaderContext.loaderIndex + 1).map(function(o) {
				return o.request;
			}).concat(loaderContext.resource || "").join("!");
		}
	});
	Object.defineProperty(loaderContext, "currentRequest", {
		enumerable: true,
		get: function() {
			return loaderContext.loaders.slice(loaderContext.loaderIndex).map(function(o) {
				return o.request;
			}).concat(loaderContext.resource || "").join("!");
		}
	});
	Object.defineProperty(loaderContext, "previousRequest", {
		enumerable: true,
		get: function() {
			return loaderContext.loaders.slice(
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值