:::info 💡 dynamic load module ::: module-federation(dynamic-module) - 图1

如何动态去加载remote.js

  • 创建远端的remoteJs文件,加载远端的资源入口文件
  • 获取远端资源变量

    loadRemote

    动态创建远端remoteJs文件,并且加载
    源自官方示例
    1. const loadRemote = (
    2. remote,
    3. shareScope,
    4. remoteFallbackUrl = undefined
    5. ) => {
    6. return new Promise((resolve, reject) => {
    7. if (!window[remote]) {
    8. const existingRemote = document.querySelector(
    9. `[data-webpack="${remote}"]`
    10. );
    11. const onload = async () => {
    12. if (!window[remote].__initialized) {
    13. // webpack 5中,__webpack_share_scopes__ 必然存在
    14. if (typeof __webpack_share_scopes__ === "undefined") {
    15. await window[remote].init(shareScope.default);
    16. } else {
    17. // __webpack_share_scopes__ 会被 编译成 __webpack_require__.S 变量
    18. // __webpack_require__.S 存储着 shard 中的资源依赖
    19. // window[remote].init 下面会说到
    20. await window[remote].init(__webpack_share_scopes__[shareScope]);
    21. }
    22. window[remote].__initialized = true;
    23. }
    24. resolve();
    25. };
    26. if (existingRemote) {
    27. existingRemote.onload = onload;
    28. existingRemote.onerror = reject;
    29. } else if (remoteFallbackUrl) {
    30. var d = document,
    31. script = d.createElement("script");
    32. script.type = "text/javascript";
    33. script.setAttribute("data-webpack", `${remote}`);
    34. script.async = true;
    35. script.onerror = reject;
    36. script.onload = onload;
    37. script.src = remoteFallbackUrl;
    38. d.getElementsByTagName("head")[0].appendChild(script);
    39. } else {
    40. reject(`Cannot Find Remote ${remote} to inject`);
    41. }
    42. } else {
    43. resolve();
    44. }
    45. });
    46. }

loadComponent

加载完remote资源,就加载需要的组件资源
源自官方示例

  1. const loadComponent = (remote, sharedScope, module, url) => {
  2. return async () => {
  3. await loadRemote(remote, sharedScope, url);
  4. const container = window[remote];
  5. // 调用remote中的资源
  6. // 方法详情在下面会说
  7. const factory = await container.get(module);
  8. const Module = factory();
  9. return Module;
  10. };

小结

动态加载远端资源,需要先加载远端的入口文件remoteJs,加载完成后,可以根据远端资源暴露出来的变量名称,去加载颗粒资源

init/get

webpack内部对remoteJs(远端资源入口文件)做的动态资源的方法

init

  1. var init = (shareScope, initScope) => {
  2. // shareScope 是当前执行init时的js上下文的__webpack_require__.S变量
  3. // 如果当前的js模块中,缺少__webpack_require__.S
  4. if (!__webpack_require__.S) return;
  5. var name = "default"
  6. // 获取当前的shard模块
  7. var oldScope = __webpack_require__.S[name];
  8. // 如果发现不相同,就报错。保证两个shared模块的统一
  9. if(oldScope && oldScope !== shareScope) throw new Error("Container initialization failed as it has already been initialized with a different share scope");
  10. // 注册进当前的__webpack_require__.S
  11. __webpack_require__.S[name] = shareScope;
  12. // 将host上的的shard资源挂载到当前remotejs中的__webpack_require__.S[name]上
  13. // __webpack_require__.I会去判断当前remoteJs中所依赖的是否在host中存在
  14. // 如果存在,就返回host中的依赖,否则就会加载自身的依赖
  15. return __webpack_require__.I(name, initScope);
  16. };

get

  1. // moduleMap 代表当前remote中暴露出的颗粒资源
  2. // 类似如下的对象
  3. // var moduleMap = {
  4. // "./Eexample": () => Promise.all([....])
  5. // }
  6. // module就是需要的颗粒资源
  7. var get = (module, getScope) => {
  8. //
  9. __webpack_require__.R = getScope;
  10. getScope = (
  11. // 如果当前存在所需要的模块资源
  12. __webpack_require__.o(moduleMap, module)
  13. // 就返回promise的资源
  14. ? moduleMap[module]()
  15. : Promise.resolve().then(() => {
  16. throw new Error('Module "' + module + '" does not exist in container.');
  17. })
  18. );
  19. __webpack_require__.R = undefined;
  20. return getScope;
  21. };