webpack联邦模块之remotes方法

使用联邦模块后当前项目就会有两个依赖,一个是依赖共享模块,一个是依赖远程模块。运行时webpack/runtime/consumes用于解析共享模块,运行时webpack/runtime/remotes 用于解析远程模块。这两个模块对应的方法分别是__webpack_require__.f.consumes ****和__webpack_require__.f.remotes,是的这两个方法都是关在__webpack_require__.f上的,和上一篇文章中的__webpack_require__.f.j在一起,都是用于加载chunk中的一环。

webpack/runtime/remotes

var chunkMapping = {"src_bootstrap_js": ["webpack/container/remote/app2/App","webpack/container/remote/app2/uitls"]
};var idToExternalAndNameMapping = {"webpack/container/remote/app2/App": ["default","./App","webpack/container/reference/app2"],"webpack/container/remote/app2/uitls": ["default","./uitls","webpack/container/reference/app2"]
};__webpack_require__.f.remotes = (chunkId, promises) => {...
}

其中数据chunkMapping使用chunkId作为key,value是module组成的数组。表示该chunk依赖这些模块。

数据idToExternalAndNameMapping则使用module作为key,value是如何加载这个远程模块。

[”default”, “./App”, “webpack/container/reference/app2”] 表示"webpack/container/remote/app2/App"模块可以从”webpack/container/reference/app2”模块的”./App”获取到,并且该远程模块依赖共享作用域”default”

一般下划线分割的字符串是chunkId,斜线分割的是moduleId

在这里__webpack_require__.f.remotes方法做的事情就是当加载chunk src_bootstrap_js时解析该chunk依赖的远程模块"webpack/container/remote/app2/App""webpack/container/remote/app2/uitls",并进行前置加载。加载完成后将这两个模块安装在当前webpack环境的__webpack_require__.modules上让代码可以通过__webpack_require__(’webpack/container/remote/app2/App’)获取到对应的远程模块导出。

**webpack_require**.f.remotes

__webpack_require__.f.remotes = (chunkId, promises) => {if(__webpack_require__.o(chunkMapping, chunkId)) {chunkMapping[chunkId].forEach((id) => {// getScope是干啥的,不知道干啥的,是不是防止有多个入口(entry),因为现在的例子是单入口的// 要把自己也分享出去才能更加复杂var getScope = __webpack_require__.R;if(!getScope) getScope = [];// data是 idToExternalAndNameMapping 的值var data = idToExternalAndNameMapping[id];if(getScope.indexOf(data) >= 0) return;getScope.push(data);if(data.p) return promises.push(data.p);var onError = (error) => {...}// 为什么要识别出是first?var handleFunction = (fn, arg1, arg2, d, next, first) => {...}var onExternal = (external, _, first) => {...}var onInitialized = (_, external, first) => ...var onFactory = (factory) => {...}handleFunction(__webpack_require__, data[2], 0, 0, onExternal, 1);}}
}

就像上面介绍的该方法用于加载入参chunkId依赖的远程模块。其核心方法是handleFunction

webpack_require.R的作用是啥?拿到是防止同一个执行两边?例如index.js中的bootstrap还有别的依赖。这样就会初始化两边了

handleFunction

var handleFunction = (fn, arg1, arg2, d, next, first) => {try {var promise = fn(arg1, arg2);if(promise && promise.then) {var p = promise.then((result) => (next(result, d)), onError);if(first) promises.push(data.p = p); else return p;} else {return next(promise, d, first);}} catch(error) {onError(error);}
}

代码行数不多,想表达的是将arg1和arg2作为入参调用函数fn,并将函数的返回值和参数d和first一起作为函数next的入参,并且兼容fn返回值为promise和非promise的情况。如果first为true,并且其返回值是promise,将其push到promises数组中。promises数组表示chunk bootstrap_js的加载完成依赖promises数组全部完成。

为什么只有first才会将promise push到promises中?

所以简单来讲handleFunction就是等fn执行完将结果作为next入参继续执行,起到了拼接两个函数顺序执行的作用。

handleFunction(__webpack_require__, data[2], 0, 0, onExternal, 1);

__webpack_require__(data[2]) (data[2]为"webpack/container/reference/app2" )执行完成后得到结果作为onExternal的入参执行该方法。

__webpack_modules__ = {"webpack/container/reference/app2": ((module, __unused_webpack_exports, __webpack_require__) => {"use strict";
var __webpack_error__ = new Error();
module.exports = new Promise((resolve, reject) => {if(typeof app2 !== "undefined") return resolve();__webpack_require__.l(app2Url + "/remoteEntry.js", (event) => {if(typeof app2 !== "undefined") return resolve();var errorType = event && (event.type === 'load' ? 'missing' : event.type);var realSrc = event && event.target && event.target.src;__webpack_error__.message = 'Loading script failed.\n(' + errorType + ': ' + realSrc + ')';__webpack_error__.name = 'ScriptExternalLoadError';__webpack_error__.type = errorType;__webpack_error__.request = realSrc;reject(__webpack_error__);}, "app2");
}).then(() => (app2));}

模块"webpack/container/reference/app2"加载完成后会得到一个对象{get: () => get, init: () => init} 这个对象就是onExternal方法的第一个入参external

onExternal

var onExternal = (external, _, first) => (external ? handleFunction(__webpack_require__.I, data[0], 0, external, onInitialized, first) : onError());

一行代码,表达的是初始化远程模块依赖的共享作用域之后执行方法onInitialized

onInitialized

var onInitialized = (_, external, first) => (handleFunction(external.get, data[1]/* ./App */, getScope, 0, onFactory, first));// external.get 方法也就是三方模块的get方法
var get = (module, getScope) => {__webpack_require__.R = getScope;getScope = (__webpack_require__.o(moduleMap, module)? moduleMap[module](): Promise.resolve().then(() => {throw new Error('Module "' + module + '" does not exist in container.');}));__webpack_require__.R = undefined;return getScope;
};var moduleMap = {"./App": () => {return Promise.all([__webpack_require__.e("webpack_sharing_consume_default_react_react-_5e40"),__webpack_require__.e("src_App_js")]).then(() => (/* external.get */**() => ((__webpack_require__(/*! ./src/App */ "./src/App.js")))**));},}

通过三方模块的get方法获取对应模块,例如这里的模块 ./App 得到返回值 () => ((__webpack_require__(/*! ./src/App */ "./App.js"))) 也就是下面的入参factory

最后执行方法onFactory

var onFactory = (factory) => {data.p = 1;// 这里可以通过id拿到远程库中的模块,将id对应的module设置上__webpack_modules__[id] = (module) => {module.exports = factory();}
};

到这里__webpack_modules__[’webpack/container/remote/app2/App’] = (module) => {module.exports = factory()} 这样三方模块安装完毕,可以保证chunk src_bootstrap_js 依赖的三方模块可以通过__webpack_require__正常获取。

__webpack_require__.I 用于初始化共享作用域当前环境和remote

该方法不仅仅会初始化自身的共享作用域,还会初始化当前项目依赖的外部项目的共享作用域:

需要注意的是__webpack_require__.I 中初始化外部作用域,并不是通过入参来知道要初始化哪些外部作用域,而是直接编译到源码中。

initExternal("webpack/container/reference/app2");
// 这样就可以通过id拿到extrnal调用使用__webpack_require__.S调用它的init方法,完成其共享作用域的初始化
__webpack_require__.S = {};
var initPromises = {};
var initTokens = {};__webpack_require__.I = (name, initScope) => {if(!initScope) initScope = [];var initToken = initTokens[name];if(!initToken) initToken = initTokens[name] = {};if(initScope.indexOf(initToken) >= 0) return;initScope.push(initToken);// only runs onceif(initPromises[name]) return initPromises[name];// creates a new share scope if neededif(!__webpack_require__.o(__webpack_require__.S, name)) __webpack_require__.S[name] = {};// runs all init snippets from all modules reachablevar scope = __webpack_require__.S[name];var warn = (msg) => ...var uniqueName = "app1";var register = (name, version, factory, eager) => {...}var initExternal = (id) => {...}var promises = [];switch(name) {case "default": {register("react-dom", "17.0.2", () => (Promise.all([]).then(() => () => {} /* factory */))));register("react", "17.0.2", () => (Promise.all([]).then(() => () /* factory */)));initExternal("webpack/container/reference/app2");}break;}
if(!promises.length) return initPromises[name] = 1;
return initPromises[name] = Promise.all(promises).then(() => (initPromises[name] = 1));

这个方法用于初始化共享作用域。在初始化当前环境共享作用域后使用方法initExternal("webpack/container/reference/app2");初始化远程模块的共享作用域。

var initPromises = {};
var initTokens = {};__webpack_require__.I = (name, initScope) => {if(!initScope) initScope = [];var initToken = initTokens[name];if(!initToken) initToken = initTokens[name] = {};if(initScope.indexOf(initToken) >= 0) return;initScope.push(initToken);...
}var initExternal = (id) => {...module.init(__webpack_require__.S[name], initScope)...
}

这几行代码不太好理解。

首先每个作用域都有一个唯一的token与之对应,在这里表现为对象。

正在初始化的作用域列表存在initScope中,如果initScope中存在这个token直接退出,表示正在初始化。(这种情况会出现在相互依赖的情况下,a 依赖 b 初始化b的共享作用域,这时候b也依赖a,b也会初始化a的作用域,这时候传入的initScope就有代表a作用域的token,否则会一直初始化下去。)

initExternal("webpack/container/reference/app2");

var initExternal = (id) => {var handleError = (err) => (warn("Initialization of sharing external failed: " + err));try {var module = __webpack_require__(id);if(!module) return;var initFn = (module) => (module && module.init && module.init(__webpack_require__.S[name], initScope))if(module.then) return promises.push(module.then(initFn, handleError));var initResult = initFn(module);if(initResult && initResult.then) return promises.push(initResult['catch'](handleError));} catch(err) { handleError(err); }
}

这段代码表达的是,等待远程模块加载完成拿到其init方法,使用当前环境的共享作用域__webpack_require__.S的某个这里是default来初始化远程环境共享作用域。

register("react", "17.0.2", () => (Promise...

var register = (name, version, factory, eager) => {var versions = scope[name] = scope[name] || {};var activeVersion = versions[version];if(!activeVersion || (!activeVersion.loaded && (!eager != !activeVersion.eager ? eager : uniqueName > activeVersion.from))) versions[version] = { get: factory, from: uniqueName, eager: !!eager };
};

向指定的共享作用域上设置包。

共享作用域初始化完成后当前环境的共享作用域形状如下:

__webpack_require__.S.default = {react: {'17.0.2': {get: () => ..., from: uniqueName, eager: boolean}},...
}

到这里加载chunk src_bootstrap_js 依赖的远程模块准备完毕,并且远程模块依赖的共享作用域同样初始化完毕。但是src_bootstrap_js还没有加载完毕,因为虽然远程模块可以通过__webpack_require__方法直接获取到,但是共享模块还无法通过该方法直接获取,因为到目前为止,共享模块只存在于__webpack_require__.S.default 上,而不存在于__webpack_modules__上。要将__webpack_require__.S.default上的共享模块在__webpack_modules__上获取还要借助接下来的方法__webpack_require__.f.consumes = (chunkId, promises) => {...}的帮助。

总结

webpack_require.f.remotes使用chunkId和promises作为入参,解析chunkId依赖的远程模块并加载和安装,让被依赖的远程模块可以通过__webpack_require__方法直接获取。

安装远程依赖的过程包括下载远程依赖和初始化远程依赖依赖的共享作用域,完善远程依赖的执行环境。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361918.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

径向菜单的制作

最终效果: 在径向菜单的制作前,首先需要知道几点知识点: Math.sin(x) x 的正玄值。返回值在 -1.0 到 1.0 之间; Math.cos(x) x 的余弦值。返回的是 -1.0 到 1.0 之间的数; 这两个函数中的X 都是指的“弧度”…

吉首大学2019年程序设计竞赛-F 天花乱坠

题目链接:https://ac.nowcoder.com/acm/contest/992/F 题意:给定正n边形,边长为100,以每条边的中点连线构成新的正n边形,无限循环下去,求所有边的长度和。 思路:简单数学计算题,可以…

SqliteHelper整理

刚开通博客不久,还没有发过文。这是第一篇,要鼓励我自己再接再厉。 另外,我也是刚刚踏上程序员这条路,有赖各位多多提携! 闲话不多说,最近参与的项目包含本地化存储这一块。昨天就园子里找了些资料有另外补…

webpack联邦模块之consumes方法

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

vue-cli使用说明

一、安装npm install -g vue-cli 推荐使用国内镜像 先设置cnpm npm install -g cnpm --registryhttps://registry.npm.taobao.org 如果安装失败,可以使用 npm cache clean 清理缓存,然后再重新安装 然后使用 cnpm 安装 vue-cli 和 webpack cnpm inst…

OptaPlanner –具有真实道路距离的车辆路线

在现实世界中,车辆路径问题(VRP)中的车辆必须走这条路:它们不能在客户之间直线行驶。 大多数VRP研究论文和演示都乐于忽略此实现细节。 和我一样,过去。 尽管使用道路距离(而不是空中距离)不会对…

自旋锁

什么是自旋锁? 自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。 …

关于如何在PSA众多请求号中查找数据是属于哪一条。

其中有两个TCODE: RSTSODS与RSTSODS,我们可以查找数据源的PSA表,然后在SE16中可以看到。 另外我们对PSA点击管理,一般会出现在窗口上面出现PSA的表名。 当然有些不在的话,那就去查找那两个TCODE。转载于:https://www.cnblogs.com/…

TCP握手为什么需要三次通信

TCP三步握手three way (or three message) handshake 是TCP核心知识点,很长一段时间内我无法理解为什么TCP建立连接需要三次通信,而不是两次或者四次或者更多次。我翻了很多问答和博客,他们说的都很有道理,但是借来的火&#xff0…

小程序用户拒绝授权解决方法

众所周知,小程序进入首先都要进行微信授权的,那万一用户不小心点了拒绝按钮怎么办呢?不要慌,官方早已预料到此情况,并提供了api供开发者使用,下面就一起来研究下api吧 一、API接口 wx.openSetting(OBJECT)…

揭示垃圾收集暂停的时间长度

有几种方法可以改善您的产品。 一种方法是仔细跟踪用户的体验并在此基础上进行改进。 我们确实自己应用了此技术,并再次花了一些时间查看不同的数据 除了我们追求的许多其他方面之外,我们还提出了一个问题“延迟GC触发应用程序的最坏情况是什么”。 为了…

异步导致UI句柄增加的解决办法

在很多操作中,都会使用到异步线程,具体怎样使用在这不说了,网上有很好的说明;本人通过Delegate.BeginInvoke实现异步调用,完成后对UI控件进行设值等,还有System.Timers.Timer都一样,使用的是线程…

[转]android ListView详解

本文转自:http://www.cnblogs.com/allin/archive/2010/05/11/1732200.html 由于google doc 很多人都打不开,故更新了源码下载地址 【源码下载】----2011-01-18 在android开发中ListView是比较常用的组件,它以列表的形式展示具体内容&#xff…

JS对象操作

一、String常用操作 1.截取 substr(start,length) //返回从指定位置开始的指定长度的字符串。 substring(start,end) //返回两个指定的位置之间的字符串。 slice(start,end) //包括字符串 stringObject 从 start 开始(包括 start)到 end 结束&#xff0…

JBoss BPM Suite 6.0.3版本的5个实用技巧

上周,红帽发布了标记为6.0.3的JBoss BPM Suite的下一版本,已订阅的用户可以在其客户门户中使用。 如果您对该版本的新增功能感到好奇,请在客户门户网站上在线查看版本说明和其余文档 。 我们正在寻找一些简单的方法来开始使用此新版本&…

package-lock.json

package.json确定依赖的范围,package-lock.json将这个范围精确到具体版本。主要是为了解决在各个环境中得到确定的node_modules,如果只依赖package.json因为该文件声明的是直接依赖的范围,它无法将直接依赖固定在某个特定版本,也无…

Linux启动管理

启动管理系统运行级别 运行级别:含义0 :关机1 :但用户模式,可以想象为windows的安全模式,主要用于系统修复2 :不完全的命令行模式,不含NFS服务3 :完全的命令行模式,就是标…

VB网站收集

1.vb源码 http://hi.baidu.com/aboutvb/home转载于:https://www.cnblogs.com/sode/archive/2011/11/05/2236883.html

Django学习---原生ajax

Ajax 原生ajax Ajax主要就是使用 【XmlHttpRequest】对象来完成请求的操作,该对象在主流浏览器中均存在(除早起的IE),Ajax首次出现IE5.5中存在(ActiveX控件)。 XmlHttpRequest对象的主要方法: a. void open(String …

The Little Schemer-周而复始之Y组合子由来

什么是递归? (define length(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l)))))))以上是length函数的实现用递归的形式计算出数据集合l的长度。 如果没有define这种赋值操作我们怎么定义length函数?换句话说我们怎么使用匿名函数完成递归…