微前端之 entry 加载工具

什么是 entry

微应用、微模块的入口

systemjs

SystemJS 是一个基于标准的模块加载器。

工作环境:

  • 在不支持原生模块的浏览器环境中,提供以 System.register 的模块格式的模块加载代码
  • single-spa 通过 systemjs 实现微模块、

特点:

  • 支持动态导入、css js json wasm 模块类型、兼容 IE11
  • 支持文件: js、css、json、wsm
  • 支持模块规范: system、umd、amd(插件支持)

import-html-enry

qiankun 框架配套开发支持 html 作为入口(entry) 的资源加载器(loader)。

工作环境:

  • qiankun 框架为了解决 JS Entry 的问题,采用更方便的 HTML Entry 方式,目的让微应用接入像 iframe 一样只需配置页面 html 的 url 地址

特点:

  • 支持文件: js、css、html
  • 支持模块规范: umd

调试 systemjs

为了方便 debugger 使用 Systemjs.import API 进行调试

  useEffect(() => {
    (async () => {
      debugger
     // 导入 js 资源
      const mobx = await Systemjs.import('https://unpkg.com/mobx@5.0.3/lib/mobx.umd.js')
      console.log(mobx);
      // 导入 css 文件
      Systemjs.import('https://unpkg.com/bootstrap@5.0.2/dist/css/bootstrap.min.css').then(module => {
        const styleSheet = module.default; // A CSSStyleSheet object
        document.adoptedStyleSheets = [...document.adoptedStyleSheets, styleSheet]; // now your css is available to be used.
        // https://developers.google.com/web/updates/2019/02/constructable-stylesheets
    })
  })()

调用 api import
  - systemJSPrototype.import 实例方法
   - preimport - 处理 script 标签
    -对 type 为 systemjs-module、systemjs-importmap 进行处理

getOrCreateLoad -> loader.instantiate(id, firstParentUrl) loader 就是 Systemjs -> systemInstantiate 判断 shouldFetch(js false, css true

进入 getOrCreateLoad

getOrCreateLoad 解析模块的依赖项进行处理创建依赖的 load 赋值在 load.d 中。

getOrCreateLoad 返回 load 对象

    // Capital letter = a promise function
    return load = loader[REGISTRY][id] = {
      id: id,
      // importerSetters, the setters functions registered to this dependency
      // we retain this to add more later
      i: importerSetters,
      // module namespace object
      n: ns, // import 导出的模块

      // instantiate
      I: instantiatePromise,
      // link
      L: linkPromise,
      // whether it has hoisted exports
      h: false,

      // On instantiate completion we have populated:
      // dependency load records
      d: undefined, // 模块依赖,需提前加载
      // execution function
      e: undefined,

      // On execution we have populated:
      // the execution error if any
      er: undefined,
      // in the case of TLA, the execution promise
      E: undefined,

      // On execution, L, I, E cleared

      // Promise for top-level completion
      C: undefined,

      // parent instantiator / executor
      p: undefined
    };

systemInstantiate 通过 shouldFetch 判断资源是否需要 fetch 来加载,js 文件类型通过 <script > 标签导入当前环境, css 文件类型返回 register

js 类型文件资源通过 创建 <script > 绑定 load 事件 appendChild 到 document 再 remove -- 资源加载完成进入 load 事件执行中

systemJSPrototype.getRegister 把资源包装成 systemjs 规范的对象

topLevelLoad 中 postOrderExec 保证 load.n 的 load.d 依赖先执行 (如果有 load.d 则需要先执行) -> doExec load.C = load.n 是执行的模块 triggerOnload(loader, load, load.er, true);

最终把导入的资源对象 register 进行拷贝赋值到 load.n = ns,返回出来

systemjs 导入其他类型文件

systemjs 除了对 js 也支持 css、json、wasm 文件资源,通过 systemJSPrototype.fetch 方法根据资源的类型判断调用 源生 fetch 获取资源包装成 systemjs 模块规范的对象返回
css 文件包装成 CSSStylesheet 对象返回

    Systemjs.import('https://unpkg.com/bootstrap@5.0.2/dist/css/bootstrap.min.css').then(module => {
      const styleSheet = module.default; // A CSSStyleSheet object
      document.adoptedStyleSheets = [...document.adoptedStyleSheets, styleSheet]; // now your css is available to be used.
      // https://developers.google.com/web/updates/2019/02/constructable-stylesheets
    })

CSSStylesheet API

分析 import-html-entry

debugger
importHTML('http://localhost:8080/example/template.html')
  .then(res => {
    console.log(res);
    console.log(res.template);
    
    res.getExternalScripts().then(r => {
      console.log('---getExternalScripts---');
      console.log(r);
    })
    
    res.getExternalStyleSheets().then(g => {
      console.log('---getExternalStyleSheets---');
      console.log(g);
    })

    debugger
    res.execScripts().then(exports => {
      console.log('---execScripts---');
      console.log(exports);
    })
  });

加载 template.html 并返回对象

import-html-entry 的源码简洁清晰,进入方法中第一眼就能看到返回值对象

{
    template: String, // html 文本
    assetPublicPath: String, // 资源的 public path
    getExternalScripts: Function () => String[], // 解析 <script> 出来的 js 代码文本数组
    getExternalStyleSheets: Function () => String[], // 解析 <style> 出来的 css 文本数组
    execScripts: Function () => Object, // 返回远程导入的 entry 对象
}

importEntry 的返回

{
  template // 返回被处理过的 html  
  assetPublichPath // 资源加载地址
  getExternalScripts // 返回解析加载过的 script 数组,包括内嵌和外联的资源
  getExternalStyleSheets // 返回解析加载过的 style 数组,包括内嵌和外联的资源
  execScripts // 返回解析 html 后的 entry,一般是最后一个 script 或者标签标识为 entry 的 js
}

template, getExternalScripts, getExternalStyleSheets

execScripts

总结

相同点:都是运行时加载
不同点:systemjs 可以通过插件加载 amd udm commonjs es6Module,import html entry 只能加载 umd

package 支持 js 模块规范 支持文件类型 工具类型 entry 区别
systemjs system、umd js、css、json、wsm 基于标准的模块加载 js entry
import-html-entry umd js、css、html 资源加载工具 html entry

systemjs 不仅是个模块加载工具更是有一套标准的模块规范,single-spa 依赖 systemjs 规范通过 js entry 实现微应用、微模块的加载。
importEntry 只支持 umd 的 js 文件,qiankun 通过 importHtml 解析 html 模板分析资源 实现 html entry 的方式加载 微应用。
import-html-entry 作为工具更适合,systemjs 是独立的模块规范 webpack rollup 打包工具都支持 systemjs 规范。
原理都是对 script 标签的资源进行 appendChild 再 remove 的方式加载到全局,对 style 标签的资源进行 fetch 获取和解析。

import-html-entry 源码方面设计的很清晰可以参考 HTML Entry 源码分析 也可以通过 debugger 的方式自己了解。

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):