webpack联邦模块之consumes方法

对于使用联邦模块的项目会有两个依赖,一个是远程模块,一个是共享模块。上一篇文章解释了远程模块的加载和安装并初始化共享作用域。consumes则是共享模块的解决方案,用于在运行时加载并安装依赖的共享模块。

为什么叫consumes?我理解是因为共享模块的获取是在__webpack_require__.I中完成的,而consumes只是使用__webpack_require__.S中的数据来安装对应模块。所以consumes是在消费__webpack_require__.S中的数据。

__webpack_require__S.default = {react: {version: {get() {}, ...}}}

以上信息并没有在__webpack_require__.m上,所以当前环境并不能使用共享作用域中的内容

var versionLt = (a, b) => {
	// see webpack/lib/util/semver.js for original code
	a=parseVersion(a),b=parseVersion(b);for(var r=0;;){if(r>=a.length)return r<b.length&&"u"!=(typeof b[r])[0];var e=a[r],n=(typeof e)[0];if(r>=b.length)return"u"==n;var t=b[r],f=(typeof t)[0];if(n!=f)return"o"==n&&"n"==f||("s"==f||"u"==n);if("o"!=n&&"u"!=n&&e!=t)return e<t;r++}
}

var findSingletonVersionKey = (scope, key) => {
	var versions = scope[key];
	return Object.keys(versions).reduce((a, b) => {
		return !a || (!versions[a].loaded && versionLt(a, b)) ? b : a;
	}, 0);
};

var satisfy = (range, version) => {
	// see webpack/lib/util/semver.js for original code
	if(0 in range){version=parseVersion(version);var e=range[0],r=e<0;r&&(e=-e-1);for(var n=0,i=1,a=!0;;i++,n++){var f,s,g=i<range.length?(typeof range[i])[0]:"";if(n>=version.length||"o"==(s=(typeof(f=version[n]))[0]))return!a||("u"==g?i>e&&!r:""==g!=r);if("u"==s){if(!a||"u"!=g)return!1}else if(a)if(g==s)if(i<=e){if(f!=range[i])return!1}else{if(r?f>range[i]:f<range[i])return!1;f!=range[i]&&(a=!1)}else if("s"!=g&&"n"!=g){if(r||i<=e)return!1;a=!1,i--}else{if(i<=e||s<g!=r)return!1;a=!1}else"s"!=g&&"n"!=g&&(a=!1,i--)}}var t=[],o=t.pop.bind(t);for(n=1;n<range.length;n++){var u=range[n];t.push(1==u?o()|o():2==u?o()&o():u?satisfy(u,version):!o())}return!!o();
}

var getSingletonVersion = (scope, scopeName, key, requiredVersion) => {
	var version = findSingletonVersionKey(scope, key);
	if (!satisfy(requiredVersion, version)) typeof console !== "undefined" && console.warn && console.warn(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion));
	return get(scope[key][version]);
};

var get = (entry) => {
	entry.loaded = 1;
	return entry.get()
};

// 给入参函数注入依赖项共享作用域scope,封装的是scope的获取,
// 因为scope可能在初始化中,也可能初始化完成
var init = (fn) => (function(scopeName, a, b, c) {
	var promise = __webpack_require__.I(scopeName);
	// 等待共享作用域初始化完成
	if (promise && promise.then) return promise.then(fn.bind(fn, scopeName, __webpack_require__.S[scopeName], a, b, c));
	return fn(scopeName, __webpack_require__.S[scopeName], a, b, c);
});

var loadSingletonVersionCheckFallback = /*#__PURE__*/ init((scopeName, scope, key, version, fallback) => {
	if(!scope || !__webpack_require__.o(scope, key)) return fallback();
	return getSingletonVersion(scope, scopeName, key, version);
});

var installedModules = {};
var moduleToHandlerMapping = {
	"webpack/sharing/consume/default/react/react?5e40": () => (loadSingletonVersionCheckFallback("default", "react", [1,17,0,2], () => (Promise.all([__webpack_require__.e("vendors-node_modules_react_index_js"), __webpack_require__.e("node_modules_object-assign_index_js")]).then(() => (() => (__webpack_require__(/*! react */ "../node_modules/react/index.js"))))))),
	"webpack/sharing/consume/default/react-dom/react-dom": () => (loadSingletonVersionCheckFallback("default", "react-dom", [1,17,0,2], () => (Promise.all([__webpack_require__.e("vendors-node_modules_react-dom_index_js"), __webpack_require__.e("webpack_sharing_consume_default_react_react")]).then(() => (() => (__webpack_require__(/*! react-dom */ "../node_modules/react-dom/index.js"))))))),
	"webpack/sharing/consume/default/react/react?7071": () => (loadSingletonVersionCheckFallback("default", "react", [4,17,0,2], () => (__webpack_require__.e("vendors-node_modules_react_index_js").then(() => (() => (__webpack_require__(/*! react */ "../node_modules/react/index.js")))))))
};
// no consumes in initial chunks
var chunkMapping = {
	"src_bootstrap_js": [
		"webpack/sharing/consume/default/react/react?5e40",
		"webpack/sharing/consume/default/react-dom/react-dom"
	],
	"webpack_sharing_consume_default_react_react": [
		"webpack/sharing/consume/default/react/react?7071"
	]
};
__webpack_require__.f.consumes = (chunkId, promises) => {
	if(__webpack_require__.o(chunkMapping, chunkId)) {
		chunkMapping[chunkId].forEach((id) => {
			if(__webpack_require__.o(installedModules, id)) return promises.push(installedModules[id]);
			var onFactory = (factory) => {
				installedModules[id] = 0;
				__webpack_require__.m[id] = (module) => {
					delete __webpack_require__.c[id];
					module.exports = factory();
				}
			};
			var onError = (error) => {
				delete installedModules[id];
				__webpack_require__.m[id] = (module) => {
					delete __webpack_require__.c[id];
					throw error;
				}
			};
			try {
				var promise = moduleToHandlerMapping[id]();
				if(promise.then) {
					promises.push(installedModules[id] = promise.then(onFactory)['catch'](onError));
				} else onFactory(promise);
			} catch(e) { onError(e); }
		});
	}
}

chunkMapping中存着chunk对应共享模块的依赖,而moduleToHandlerMapping中存着共享模块的获取方式,通过方法__webpack_require__.f.consumes入参这两个数据就能解析chunk依赖的模块,并对模块进行安装。

(loadSingletonVersionCheckFallback("default", "react", [1,17,0,2], fallback))

这一行表达的是在共享作用域default上寻找版本为1.17.0.2的react,如果找不到使用自带的模块fallback。

var init = (fn) => (function(scopeName, a, b, c) {
	var promise = __webpack_require__.I(scopeName);
	// 等待共享作用域初始化完成
	if (promise && promise.then) return promise.then(fn.bind(fn, scopeName, __webpack_require__.S[scopeName], a, b, c));
	return fn(scopeName, __webpack_require__.S[scopeName], a, b, c);
});

var loadSingletonVersionCheckFallback = /*#__PURE__*/ init((scopeName, scope, key, version, fallback) => {
	if(!scope || !__webpack_require__.o(scope, key)) return fallback();
	return getSingletonVersion(scope, scopeName, key, version);
});

init方法用于初始化对应共享作用域,共享作用域加载完成后将相关作用域信息作为入参调用init的入参函数。

拿到作用域信息后调用方法getSingletonVersion(scope, scopeName, key, version)

getSingletonVersion

function getSingletonVersion(scope, scopeName, key, requiredVersion) {
	...
	return get(scope[key][version]);
}

var get = (entry) => {
	entry.loaded = 1;
	return entry.get();
};

get方法的返回值会作为方法onFactory的入参

var onFactory = (factory) => {
	installedModules[id] = 0;
	__webpack_require__.m[id] = (module) => {
		delete __webpack_require__.c[id];
		module.exports = factory();
	}
};

onFactory执行完成后__webpack_require__.m上就会出现对应的模块信息。到这里共享模块也安装完成了。

等待chunk src_bootstrap_js文件加载完成,外部依赖准备完成,共享模块准备完成后chunk src_bootstrap_js 则表示加载完成可以开始执行业务代码了。

名字的翻译

在源码中使用的名字是 import React from ‘react’,而通过webpack编译后变成了”webpack/sharing/consume/default/react/react?5e40”,并且在__webpack_require__.S上的名字也是react,这些是怎么关联起来的呢?

该问题同样存在于远程模块加载场景中。

”webpack/sharing/consume/default/react/react?5e40”是怎么和共享作用域中的”react”关联起来的?

在上面源码中

var moduleToHandlerMapping = {
	"webpack/sharing/consume/default/react/react?5e40": () => (loadSingletonVersionCheckFallback("default", "react", [1,17,0,2], () => (Promise.all([__webpack_require__.e("vendors-node_modules_react_index_js"), __webpack_require__.e("node_modules_object-assign_index_js")]).then(() => (() => (__webpack_require__(/*! react */ "../node_modules/react/index.js"))))))),
}

表示模块"webpack/sharing/consume/default/react/react?5e40"需要在共享作用域default中的1.17.0.2版本的react,这里这两个名字就关联起来了。

源码中的 import React from ‘react’ 是怎么和 "webpack/sharing/consume/default/react/react?5e40" 关联起来的?

源码中的

import React from 'react'

经过编译变成

var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react/react?5e40");
var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);

这样源码中的名字 ”react”就和"webpack/sharing/consume/default/react/react?5e40"关联起来了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值