微前端技术说明

微前端技术说明

兰涛 lands

什么是微前端

微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。

微前端架构具备以下几个核心特点:

  • 技术栈无关 主框架不限制接入应用的技术栈,微应用具备完全自主权
  • 独立开发、独立部署 微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
  • 增量升级
  • 在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
  • 独立运行时 每个微应用之间状态隔离,运行时状态不共享

微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith )后,随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤其常见。

为什么需要微前端

微前端和后端微服务一样会带来大量的挑战

微前端的应用最主要的是为了功能模块的拆分,拆分的目的不是为了架构上好看,是为子系统之间独立

  • 微应用技术独立:可用不同技术栈同时开发多个系统
  • 独立上线互不干扰:子应用开发完成后,可以独立上线,不需要等待其他子应用的进度
  • 解决巨石应用问题:避免后期迭代的时候项目逐渐臃肿,导致打包时间长,降低开发效率

原理

image.png

根本上就是,父应用加载子应用的入口,这个入口就是一段 js 代码,这段 js 代码会异步的加载出对应的 HTML 和相应资源。

qiankun

qiankun 是一个基于 single-spa 微前端 实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。

HTML Entry 接入方式,让你接入微应用像使用 iframe 一样简单。

为什么不适用 Iframe 接入方式?

qiankun 官方解释:

为什么不用 iframe,这几乎是所有微前端方案第一个会被 challenge 的问题。但是大部分微前端方案又不约而同放弃了 iframe 方案,自然是有原因的,并不是为了 “炫技” 或者刻意追求 “特立独行”。

如果不考虑体验问题,iframe 几乎是最完美的微 前端 解决方案了。

iframe 最大的特性就是提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。但他的最大问题也在于他的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题。

  1. url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
  2. UI 不同步,DOM 结构不共享。想象一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时我们要求这个弹框要浏览器居中显示,还要浏览器 resize 时自动居中…
  3. 全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。
  4. 慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。

其中有的问题比较好解决(问题 1),有的问题我们可以睁一只眼闭一只眼(问题 4),但有的问题我们则很难解决(问题 3)甚至无法解决(问题 2),而这些无法解决的问题恰恰又会给产品带来非常严重的体验问题, 最终导致我们舍弃了 iframe 方案。

@umijs/plugin-qiankun

umi 生态的 qiankun 的一个插件,能快速帮助项目实现微前端。

实现效果

这里暂时只放了视频,由于现在的模版代码中包含了服务器信息,所以无法开源。后期整理后我会将代码开源到 github。

在这个主子应用在实现了高度的集成,日后的开发只需要拉取一个子系统专注于业务。

vgm67-or3vf.gif

父子应用各自特点

主应用责任范围说明

  • 主应用包含完整的菜单功能(菜单需要单独实现,不能通过路由生成)
  • 主应用路由配置,只需要配置好对应的子应用和自己应用的页面路由
  • 主应用包含自己的样式管理,并且向其他应用通知自己的变化
  • 主应用包含自己的快捷菜单记录栏
  • 有自己的登录体系
  • 有和子应用呼应的监听者模式(“eventemitter3”)
  • 不负责业务页面实现

主应用功能点说明

  • 菜单实现:需要在 app.tsx 中 layou 对象的 menu 中去自己实现。也可以在其他任何允许的地方。它包含完整的主应用 path 和子应用 path
  • Prolayout 主题和各样式设置:正常像单应用一样设置就可以。需注意:将自己的各种状态通过 app.ts/useQiankunStateForSlave 返回给子应用
  • 快捷菜单栏访问:使用监听者模式,通过 npm eventemitter3 实现
    • 在父子应用中,除了官方的通信模式、可以有其他形式的通信,比如:localStorage 类、监听则模式

@umijs/max 模板项目,更多功能参考 Umi Max 简介

系统级子应用责任范围说明(前提:此子系统能独立于主应用运行)

  • 有自己的登录体系
  • 有自己的菜单路由体系
  • 有自己的样式主题体系
  • 能响应主应用的样式同步
  • 能处理主应用的数据同步
  • 负责业务页面的实现
  • 负责面包屑的自定义实现
  • 负责页面 title 的自定义实现

功能子应用子应用责任范围说明(例如:网页右下角嵌入聊天系统)

  • 保证父子应用的数据同步和 UI 同步

子应用功能点说明

  • 样式同步在 src/layout.tsx 文件中通过 hooks useModel(‘@@qiankunStateFromMaster’)
    • 色彩主题在此处处理
    • 子应用的菜单栏的是否显示可在此处,通过 useModel(‘@@qiankunStateFromMaster’) 进行复验和矫正(可选)
  • 初始化数据在 app.tsx 中使用全局变量接受生命周期中主应用的状态。
  • 数据同步根据具体业务和时机具体处理
  • 菜单栏的是否显示也可在 app.ts/layout,通过 url 或 localstorage 判断是否显示
  • 子应用需要实现自己的面包屑(可选),如果不自定义实现:面包屑的第一级会根据菜单的第二级开始显示。原因:ProLayout 是主应用中的,PageContainer 是子应用中的,完整结构是 master ProLayout -> micro ProLayout(UI 隐藏了,但是功能还在) -> micro PageContainer

问题和解决方案

Q1: 微前端的模块拆分问题?

A:拆分力度越小,意味着架构越复杂,维护成本就会更高

Q2: 技术栈一旦多样化,意味着可能出现混乱

A:只是用 Vue 和 react 技术栈,其中全局组件封装在 npm 私有库。各子应用之间使用同一 UI 库,至于后期的 UI 库的版本升级,可逐步迭代老项目或者放弃迭代,具体看当时的获得和付出评估。

注:vue 技术使用Umi-vue

Q3: 微前端中的样式隔离问题? A:微前端架构中的样式隔离问题通常是指不同微前端应用之间样式相互干扰的问题。解决这个问题的方法有多种:

  1. 模块内使用 CSS Modules

使用 CSS Modules 技术可以有效地解决微前端架构中的样式隔离问题。CSS Modules 可以将样式文件和组件代码绑定在一起,从而使每个组件的样式都只应用于自己的组件。这种方法不需要对现有的应用程序进行大量的修改,可以轻松地引入到现有的项目中。

  1. CSS 命名约定

使用命名约定可以帮助解决微前端架构中的样式隔离问题。例如,在应用程序中使用唯一的类名和 ID,可以避免不同组件之间的样式冲突。此外,也可以使用 BEM(Block Element Modifier)等命名约定来帮助管理应用程序的样式。

实现方式,以 antd 为例,采用修改 Webpack 的 css-loader,统一添加样式前缀。在用过 antd主题定制 方式添加 dom 的样式前缀

配置 webpack 修改 less 变量

1
2
3
4
5
6
7
8
9
{
loader: 'less-loader',
+ options: {
+ modifyVars: {
+ '@ant-prefix': 'yourPrefix',
+ },
+ javascriptEnabled: true,
+ },
}

配置 antd ConfigProvider

1
2
3
4
5
6
7
import { ConfigProvider } from 'antd';

export const MyApp = () => (
<ConfigProvider prefixCls="yourPrefix">
<App />
</ConfigProvider>
);

Q4: 共享资源耦合性问题?

A: 介绍:由于每个微应用都是独立的,因此它们可能需要共享一些公共资源,如样式表、JavaScript 库、图像等。这些公共资源可以在多个应用程序之间共享,以减少代码的重复,并提高开发效率。然而,共享资源也可能会产生一些问题,如不良的耦合和依赖关系,增加代码复杂性和难以调试等。

共享资源解决方案:

  1. 定义公共资源库:在微前端架构中,应该为所有应用程序定义一个公共资源库,以避免重复代码。这个库应该包含所有的公共资源,例如样式表、JavaScript 库和图像等。
  2. 明确资源拥有者:在定义公共资源库时,应该明确每个资源的拥有者。这样可以避免不必要的依赖和版本冲突。每个资源都应该有一个明确的责任人,并且应该有一个明确的更新和维护计划。
  3. 遵循版本管理最佳实践:在共享资源时,应该遵循版本管理最佳实践。每个资源都应该有一个版本号,并且应该在需要升级时进行适当的版本控制。同时,应该确保每个应用程序使用的是正确的版本,以避免版本冲突和依赖关系。

Q5: 父子应用通信?

A1: 基于 useModel() 的通信。通过umi 脚手架搭建的项目很方便接入

A2: 基于配置的通信。适用于,子应用不通过 umi 搭建的项目,可直接接入qiankun

具体实现方案,见:父子组件通信

更多乾坤的优化原理:https://www.yuque.com/kuitos/gky7yw/gs4okg

  • 标题: 微前端技术说明
  • 作者: 兰涛
  • 创建于 : 2023-07-07 15:57:25
  • 更新于 : 2024-01-26 09:53:48
  • 链接: https://lands.work/6cc68f77/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论