在微前端中加载 Vite 应用

简介: 自 2018 年 5 月 Firefox 60 发布后,所有主浏览器均默认支持 ES modules。借助 ES modules 的能力,代码可以实现无需构建直接运行。随着 Vite 和 Snowpack 等基于 ES modules 的构建工具的产生,前端随即掀起了 ES modules 新一轮热潮。
来源:Alibaba F2E公众号
作者:那吒

image.png

自 2018 年 5 月 Firefox 60 发布后,所有主浏览器均默认支持 ES modules。借助 ES modules 的能力,代码可以实现无需构建直接运行。

随着 Vite 和 Snowpack 等基于 ES modules 的构建工具的产生,前端随即掀起了 ES modules 新一轮热潮。

image.png

问题背景

天下武功,无招不破,唯快不破 - 李小龙

Vite、Snowpack 等基于 ES modules 的构建工具带来了开发的极致体验,相比传统的构建工具,这些新型的构建工具或多或少地带来了以下优势:

  • 由于无需打包的特性,服务器冷启动时间超快

借助 ES modules 的能力,模块化交给浏览器处理(虽然目前的阶段存在一个预编译的过程)。传统构建器需要打包依赖和源码,才能构建整个应用,并提供服务。

  • 项目大小不再成为限制项目热更新速度的因素

传统构建器在代码更改时,需要重新构建并载入页面,这样带来的的结果是:随着项目体积增长,构建耗时越长。基于 ES modules 的构建器只进行单文件编译,单文件更新,时间复杂度保持 O(1).

Vite 得益于原生 ES modules 的能力,大幅提升了开发时体验。相信未来,随着社区生态(CDN 服务、Deno)、ESM 相关标准(import-maps、import.meta)的逐步完善,以及越来越多的技术方案解决 ES modules 在浏览器端的相关难题(依赖瀑布,资源碎片化),前端会开启一个无构建的新篇章。

同时在微前端领域,脚本资源的打包规范向来是百花齐放(比如 singleSPA 默认支持 SystemJs 规范,icestark 默认支持 UMD 规范)。未来脚本资源的打包规范必定是趋于统一的 ES modules 规范。正是基于这两个原因,微前端支持 ES modules 应用的加载就成了用户强诉求。

微前端加载 Vite 应用

加载 ES modules 微应用

Vite 会默认打包出符合标准的 ES modules 的脚本资源。ES modules 资源的加载方式如下:

<script src="index.js" type="module"></script>

然而,在 icestark 中需要依赖微应用导出 生命周期函数 来渲染微应用。使用

const { mount, unmount } = await import(url);

Dynamic Import 的浏览器兼容性如下:

image.png

可以认为,支持 ES modules 的浏览器版本,对 Dynamic Import 的支持也非常良好。同时,为了兼容旧版浏览器,通过 new Function() 将其包裹:

const dynamicImport = new Function('url', 'return import(url)');

const { mount, unmount } = await dynamicImport(url);

至此,除了能支持 IIFE / UMD 规范的微应用之外,icestark 支持了 ES modules 规范的应用加载,并通过 import 类型标识。icestark 整体加载流程图如下:

image.png

Vite 应用的改造

对于微应用而言,需要导出 生命周期函数,并选择合适的加载方式即可。

生命周期函数的接入非常简单,在 Vite 应用的入口文件(Vue 项目通常是 main.t|js,React 应用通常是 app.t|jsx)声明函数(以 Vue 应用为例):

import { createApp } from 'vue'
+ import type { App as Root} from 'vue';
import App from './App.vue'
+ import isInIcestark from '@ice/stark-app/lib/isInIcestark';
- createApp(App).mount('#app');

+ let vue: Root<Element> | null = null;

+ if (!isInIcestark()) {
+   createApp(App).mount('#app');
+ }
+ // 导出 mount 生命周期函数
+ export function mount({ container }: { container: Element}) {
+   vue = createApp(App);
+    vue.mount(container);
+ }
+ // 导出 unmount 生命周期函数
+ export function unmount() {
+    if (vue) {
+     vue.unmount();
+    }
}

然而,在实际构建过程中,我们发现声明的函数并没有在脚本资源中导出。这是个非常疑惑的点,让我们深入到 Vite 的源码,并在内置的 vite:build-html 找寻到一些蛛丝马迹:

...
if (isModule) {
  inlineModuleIndex++
  if (url && !isExcludedUrl(url)) {
  // <script type="module" src="..."/>
  // add it as an import
  js += `\nimport ${JSON.stringify(url)}`
  shouldRemove = true
  } else if (node.children.length) {
  // <script type="module">...</script>
  js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}.js"`
  shouldRemove = true
  }
}
...

Vite 默认使用 index.html 作为入口,在解析 index.html 的过程中,会生成一个虚拟的入口文件,将脚本资源通过 import 注入进来,也就是最终的入口文件实际上类似于下面的代码:

import './src/main.ts';
import 'polyfill';

面对这个场景,我们想到了两种解决方案:

  • 借助 Vite Lib 模式,修改应用入口:
// vite.config.ts
export default defineConfig({
  ...
  build: {
    lib: {
      entry: './src/main.ts',
      formats: ['es'],
      fileName: 'index'
    },
    rollupOptions: {
      preserveEntrySignatures: 'exports-only'
    }
  },
})

这种方式有个明显的问题是:Vite 以 Lib 模式构建出的应用,其产物并不是一个完整的前端应用(缺少 index.html),无法满足独立运行的条件。

  • 通过插件修改 Vite 的这一默认行为

通过 vite-plugin-index-html 插件,结合 Vite 的解析能力,将入口修改为静态资源的入口。

import htmlPlugin from 'vite-plugin-index-html';

// vite.config.ts
export default defineConfig({
  plugins: [vue(), htmlPlugin({
    input: './src/main.ts'
  })]
})

ice.js Vite 模式

同时,icestark 也支持 ice.js Vite 模式快速接入。安装或升级 build-plugin-icestark 插件,在微应用 build.json 中配置:

{
+  "vite": true,
  "plugins": [
    ["build-plugin-icestark", {
      "type": "child",
    }]
  ]
}

即可得到正确导出生命周期函数的微应用。详细用法可参见 使用 ice.js Vite 模式。

最终效果

效果展示

你将得到什么

渐进升级

为了解决时间上,长尾应用升级带来的效率问题,微前端通常是大型架构升级所选择的中间态(或终态)方案。因此在设计加载 ES modules 方案时,需要保持这一基准原则。

框架应用可以保持现有的构建方式不变(仍然可以使用 webpack 等非原生 ES modules 构建工具),亦无需对框架应用做任何构建上的改造。

因此,基本可以无痛尝试 Vite 所带来的快感,脚踏实地地,一点点地靠近远方。

二次加载的极致体验

通过对 ES modules 原理的探寻,可以知道 ES modules 只执行一次。换成实际例子,也就是说当第二次执行相同的加载脚本时:

// icestark 第二次执行加载脚本
const { mount, unmout } = import(esModule);

浏览器不会重复执行 Construction -> Instantiation -> Evaluation 的流程,而是直接返回上次模块执行的结果。这会导致一些副作用的操作(比如在 Module Conext 下插入样式资源,脚本资源的行为,这给我们的微应用二次加载带来了额外的问题),同时也带来了极快的二次加载效果。效果展示

写在最后

建立在原生 ES modules 规范下的应用不会在短时间内快速铺开,很多 To C,To 商户的业务对浏览器的版本仍有限制。但是,icestark 在 2.x 快一年多的发展以来,仍希望覆盖到多样的开发场景,提供便捷、快速地业务升级。在支持传统 JS bundle、UMD 规范,本文分享了 icestark 在接入 ES modules 规范微应用的一些尝试,希望能给开发者带来一些新的选择和启发。

引用

  • icestark - 面向大型系统的微前端解决方案
  • proposal-dynamic-import
  • Vite - Next Generation Frontend Tooling
  • ES modules: A cartoon deep-dive
  • What Happens When a Module Is Imported Twice?
相关文章
|
5天前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
|
5天前
|
前端开发 机器人 API
前端大模型入门(一):用 js+langchain 构建基于 LLM 的应用
本文介绍了大语言模型(LLM)的HTTP API流式调用机制及其在前端的实现方法。通过流式调用,服务器可以逐步发送生成的文本内容,前端则实时处理并展示这些数据块,从而提升用户体验和实时性。文章详细讲解了如何使用`fetch`发起流式请求、处理响应流数据、逐步更新界面、处理中断和错误,以及优化用户交互。流式调用特别适用于聊天机器人、搜索建议等应用场景,能够显著减少用户的等待时间,增强交互性。
|
4天前
|
存储 监控 前端开发
掌握微前端架构:构建未来前端应用的基石
【10月更文挑战第12天】随着前端技术的发展,传统的单体应用架构已无法满足现代应用的需求。微前端架构通过将大型应用拆分为独立的小模块,提供了更高的灵活性、可维护性和快速迭代能力。本文介绍了微前端架构的概念、核心优势及实施步骤,并探讨了其在复杂应用中的应用及实战技巧。
|
5天前
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。
|
5天前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
5天前
|
机器学习/深度学习 弹性计算 自然语言处理
前端大模型应用笔记(二):最新llama3.2小参数版本1B的古董机测试 - 支持128K上下文,表现优异,和移动端更配
llama3.1支持128K上下文,6万字+输入,适用于多种场景。模型能力超出预期,但处理中文时需加中英翻译。测试显示,其英文支持较好,中文则需改进。llama3.2 1B参数量小,适合移动端和资源受限环境,可在阿里云2vCPU和4G ECS上运行。
|
5天前
|
前端开发 算法 测试技术
前端大模型应用笔记(五):大模型基础能力大比拼-计数篇-通义千文 vs 文心一言 vs 智谱 vs 讯飞vsGPT
本文对比测试了通义千文、文心一言、智谱和讯飞等多个国产大模型在处理基础计数问题上的表现,特别是通过链式推理(COT)提示的效果。结果显示,GPTo1-mini、文心一言3.5和讯飞4.0Ultra在首轮测试中表现优秀,而其他模型在COT提示后也能显著提升正确率,唯有讯飞4.0-Lite表现不佳。测试强调了COT在提升模型逻辑推理能力中的重要性,并指出免费版本中智谱GLM较为可靠。
前端大模型应用笔记(五):大模型基础能力大比拼-计数篇-通义千文 vs 文心一言 vs 智谱 vs 讯飞vsGPT
|
4天前
|
资源调度 前端开发 JavaScript
Vite:新一代前端构建工具的革命性体验
【10月更文挑战第13天】Vite:新一代前端构建工具的革命性体验
|
4天前
|
前端开发 JavaScript 中间件
Vite:下一代前端构建工具的崛起
【10月更文挑战第13天】Vite:下一代前端构建工具的崛起
|
4天前
|
JSON 前端开发 JavaScript
Vite:新一代前端构建工具的崛起
【10月更文挑战第13天】Vite:新一代前端构建工具的崛起