Skip to content

Vite模块联邦打包源码分析

本地项目拉取远程模块

  1. 首选声明了一个远程映射, 这是本地项目生成的
js
const remotesMap = {
  'remote-simple':{url:'http://localhost:8089/assets/remoteEntry.js',format:'esm',from:'vite'}
};
const remotesMap = {
  'remote-simple':{url:'http://localhost:8089/assets/remoteEntry.js',format:'esm',from:'vite'}
};
  1. 如果你import导入了一个组件,则会继续执行
js
import Button from 'remote-simple/remote-simple-button'
import Button from 'remote-simple/remote-simple-button'
js
getRemote("remote-simple" , "./remote-simple-button")
getRemote("remote-simple" , "./remote-simple-button")
  1. 接着执行 ensure 方法加载远程代码
js
ensure(remoteName)
  .then((remote) => remote.get(componentName)
  .then(factory => factory()))
ensure(remoteName)
  .then((remote) => remote.get(componentName)
  .then(factory => factory()))
  1. ensure中,会异步加载js代码
js
async function __federation_method_ensure(remoteId) {
  const remote = remotesMap[remoteId];
  if (!remote.inited) {
    if (scriptTypes.includes(remote.format)) {
      // 如果代码模块是 iife 或 umd

      return new Promise(resolve => {
        const callback = () => {
          if (!remote.inited) {
            remote.lib = window[remoteId];
            remote.lib.init(wrapShareModule(remote.from));
            remote.inited = true;
          }
          resolve(remote.lib);
        };
        return loadJS(remote.url, callback);
      });
    } else if (importTypes.includes(remote.format)) {
      return new Promise(resolve => {
        const getUrl = typeof remote.url === 'function' ? remote.url : () => Promise.resolve(remote.url);
        getUrl().then(url => {
          __vitePreload(() => import(/* @vite-ignore */ url),true?[]:void 0).then(lib => {
            if (!remote.inited) {
              const shareScope = wrapShareModule(remote.from);
              lib.init(shareScope);
              remote.lib = lib;
              remote.lib.init(shareScope);
              remote.inited = true;
            }
            resolve(remote.lib);
          });
        });
      })
    }
  } else {
    return remote.lib;
  }
}
async function __federation_method_ensure(remoteId) {
  const remote = remotesMap[remoteId];
  if (!remote.inited) {
    if (scriptTypes.includes(remote.format)) {
      // 如果代码模块是 iife 或 umd

      return new Promise(resolve => {
        const callback = () => {
          if (!remote.inited) {
            remote.lib = window[remoteId];
            remote.lib.init(wrapShareModule(remote.from));
            remote.inited = true;
          }
          resolve(remote.lib);
        };
        return loadJS(remote.url, callback);
      });
    } else if (importTypes.includes(remote.format)) {
      return new Promise(resolve => {
        const getUrl = typeof remote.url === 'function' ? remote.url : () => Promise.resolve(remote.url);
        getUrl().then(url => {
          __vitePreload(() => import(/* @vite-ignore */ url),true?[]:void 0).then(lib => {
            if (!remote.inited) {
              const shareScope = wrapShareModule(remote.from);
              lib.init(shareScope);
              remote.lib = lib;
              remote.lib.init(shareScope);
              remote.inited = true;
            }
            resolve(remote.lib);
          });
        });
      })
    }
  } else {
    return remote.lib;
  }
}
  • 如果代码是umd或iife模块规范,会调用loadJS,创建script标签进行加载
js
const callback = () => {
  if (!remote.inited) {
    remote.lib = window[remoteId];
    remote.lib.init(wrapShareModule(remote.from));
    remote.inited = true;
  }
  resolve(remote.lib);
};
return loadJS(remote.url, callback);
const callback = () => {
  if (!remote.inited) {
    remote.lib = window[remoteId];
    remote.lib.init(wrapShareModule(remote.from));
    remote.inited = true;
  }
  resolve(remote.lib);
};
return loadJS(remote.url, callback);
  • 如果代码是 esm 或 systemjs,则会通过动态import加载。会经过如下步骤

    • 首先调用__vitePreload预加载入口模块脚本

    • 入口模块加载完成后,获取远程模块的module对象, 并执行上面的init方法,把共享模块信息传进去,会将共享模块信息挂载在window.__federation_shared__.default上面

    • 将lib共享模块,挂载在remote.lib = lib

    • 然后再执行 vue 的init方法,找到vue依赖的lib, 同样挂在__federation_shared__上面,这样就把所有依赖平铺了。

以上操作都是为了,提前初始化好lib共享依赖

js
const getUrl = typeof remote.url === 'function' ? remote.url : () => Promise.resolve(remote.url);
getUrl().then(url => {
  __vitePreload(() => import(/* @vite-ignore */ url),true?[]:void 0).then(lib => {
    if (!remote.inited) {
      const shareScope = wrapShareModule(remote.from);
      lib.init(shareScope);
      remote.lib = lib;
      remote.lib.init(shareScope);
      remote.inited = true;
    }
    resolve(remote.lib);
  });
});
const getUrl = typeof remote.url === 'function' ? remote.url : () => Promise.resolve(remote.url);
getUrl().then(url => {
  __vitePreload(() => import(/* @vite-ignore */ url),true?[]:void 0).then(lib => {
    if (!remote.inited) {
      const shareScope = wrapShareModule(remote.from);
      lib.init(shareScope);
      remote.lib = lib;
      remote.lib.init(shareScope);
      remote.inited = true;
    }
    resolve(remote.lib);
  });
});
  1. 调用remote.get(componentName)加载组件模块