什么是微前端?

微前端(Micro Frontends)是前端领域中的一种架构思想,类似于微服务(Microservices)在后端的应用。微前端的主要目标是将一个大的前端应用拆分成若干个独立的、可部署的、技术栈不同的子应用(微应用),这些子应用可以独立开发、测试、部署和运行,最终通过某种机制集成到一个统一的用户界面上。

微前端的核心思想:

  1. 独立开发、独立部署:每个微应用可以独立开发和部署。
  2. 技术栈无关:每个微应用可以使用不同的技术栈,比如 Vue、React、Angular 可以混合使用。
  3. 解耦与模块化:通过微前端架构,解耦大型应用,提升可维护性。
  4. 独立运行:每个微应用可以独立运行,不依赖其他微应用。

常用的微前端框架及技术方案

常见的微前端框架有:

  1. Single-SPA
  2. Qiankun
  3. Module Federation(Webpack 5)
  4. Iframe-Based

Single-SPA

简介:

Single-SPA 是最早流行的微前端框架之一,它的核心理念是将不同的前端应用(如 React、Vue、Angular)注册为子应用,并且基于路由来动态加载这些子应用。Single-SPA 本身不会限定你的技术栈,你可以自由组合使用不同的框架。

优点:

  • 支持多个前端框架共存(React、Vue、Angular 等)。
  • 开发社区活跃,有较多的插件和文档。
  • 良好的模块隔离机制,可以帮助管理子应用的生命周期。

缺点:

  • 需要手动配置,开发者需要了解一定的底层实现。
  • 子应用之间的依赖可能导致较复杂的通信和共享机制。
  • 学习曲线稍高。

示例:

一个简单的 Single-SPA 项目可能包括两个子应用:一个 React 应用和一个 Vue 应用。

1
npm install single-spa

配置子应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { registerApplication, start } from 'single-spa';

registerApplication({
name: '@my-app/react-app',
app: () => import('path-to-react-app'),
activeWhen: ['/react'],
});

registerApplication({
name: '@my-app/vue-app',
app: () => import('path-to-vue-app'),
activeWhen: ['/vue'],
});

start();

实现步骤:

  1. 创建一个主应用(root config)。
  2. 注册各个子应用,并通过路由来加载不同的子应用。
  3. 使用 start() 启动微前端系统。

Qiankun

Qiankun 是基于 Single-SPA 之上的一套微前端框架,由阿里巴巴蚂蚁金服团队开发。它的特点是开箱即用,提供了更简单的 API 和更强的隔离机制,使开发者可以快速搭建微前端应用。相比 Single-SPA,Qiankun 的使用更加简单,并且提供了很多额外的功能。

优点:

  • 基于 Single-SPA,但更易用,配置更加简洁。
  • 提供了样式隔离、JS 沙箱等增强功能,确保子应用之间的隔离。
  • 提供了丰富的文档和教程,易于上手。
  • 支持子应用的独立开发和调试。

缺点:

  • 对于复杂场景,仍然需要开发者做一些额外的配置。
  • 尽管 Qiankun 解决了一些单体架构的问题,但子应用之间的依赖和耦合仍需开发者处理。

示例:

一个简单的 Qiankun 主应用和子应用的配置:

安装 Qiankun:

1
npm install qiankun

主应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
{
name: 'reactApp',
entry: '//localhost:7100',
container: '#container',
activeRule: '/react',
},
{
name: 'vueApp',
entry: '//localhost:7101',
container: '#container',
activeRule: '/vue',
},
]);

start();

子应用(如 React 应用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { render } from 'react-dom';
import App from './App';

function renderApp() {
render(<App />, document.getElementById('root'));
}

if (!window.__POWERED_BY_QIANKUN__) {
renderApp();
}

export async function bootstrap() {}
export async function mount() {
renderApp();
}
export async function unmount() {
const root = document.getElementById('root');
root.innerHTML = '';
}

实现步骤:

  1. 安装 qiankun,并在主应用中注册子应用。
  2. 每个子应用实现 bootstrapmountunmount 等生命周期方法。
  3. 使用 start() 启动微前端应用。

qiankun实现样式隔离的方法

动态样式表隔离

当一个子应用加载时,Qiankun 会拦截该子应用中所有的 stylelink 标签,并将它们动态挂载到当前子应用的 DOM 结构内,而不是全局作用域。每当子应用被卸载时,Qiankun 会移除这些样式表,确保它们不会影响其他子应用或主应用的样式。

  1. 动态样式表隔离(Dynamic StyleSheet Isolation):通过动态处理 stylelink 标签,确保子应用的样式只在它的作用域中生效。
  2. CSS 沙箱(CSS Isolation with Shadow DOM,实验性):通过 Shadow DOM 技术进一步隔离样式,使其与外部完全分离。

CSS 沙箱(Shadow DOM 隔离)

每个子应用都可以被封装到一个 shadowRoot 中,shadowRoot 是一个独立的 DOM 子树,具有自己独立的样式作用域。其内部样式不会影响外部,也不会被外部样式影响。当子应用被封装在 Shadow DOM 中时,该子应用的所有样式都只作用于该 shadowRoot 内的 DOM 元素。

Module Federation(Webpack 5)

Module Federation 是 Webpack 5 中引入的功能,它允许多个独立打包的应用共享模块和依赖。这使得微前端应用之间可以高效地共享代码,比如在不同的子应用中共享某个库或组件,而不需要重复打包。

优点:

  • 基于 Webpack 原生支持,使用时无需额外的微前端框架。
  • 允许多个应用共享依赖和模块,减少重复代码和加载时间。
  • 支持独立开发、独立部署,并且可以动态加载模块。

缺点:

  • 对 Webpack 配置要求较高,开发者需要深入了解 Webpack。
  • 仅支持 Webpack 项目,限制了一些非 Webpack 构建的项目。

示例:

使用 Webpack 5 Module Federation 配置微前端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// webpack.config.js (主应用)
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
app1: 'app1@http://localhost:3001/remoteEntry.js',
},
}),
],
};

// webpack.config.js (子应用)
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/Button',
},
}),
],
};

实现步骤:

  1. 在 Webpack 配置中使用 ModuleFederationPlugin
  2. 定义主应用和子应用之间的模块共享规则。
  3. 每个子应用都打包为独立的模块,主应用可以动态加载和使用这些模块。

Iframe微前端

使用 <iframe> 标签是实现微前端最简单的方式。通过将不同的子应用嵌入到 iframe 中,可以轻松实现隔离和独立部署。不同子应用之间不会共享样式和 JS 全局变量,因为 iframe 天然隔离了作用域。

优点:

  • 完全隔离:每个子应用有自己独立的作用域,避免样式和 JS 的污染。
  • 实现简单:使用浏览器的原生 iframe 标签,无需额外依赖。

缺点:

  • 体验较差:iframe 导致跨子应用的通信变得复杂。
  • 性能问题:iframe 的渲染性能较差,且嵌套 iframe 会导致加载时间增加。
  • SEO 友好性差:iframe 中的内容不利于 SEO 优化。
  • url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。当使用iFrame嵌入内容时,浏览器会默认缓存iFrame加载的页面。这意味着当嵌入的页面内容发生更新时,浏览器可能仍然展示旧内容,而不是新内容。这是因为浏览器认为嵌入的页面没有发生变化,因此加载缓存中的旧版本。解决iFrame缓存问题的一种常用方法是通过在iFrame的URL中添加随机参数来强制浏览器重新加载内容。这样,每次加载iFrame时,URL都会不同,浏览器会认为内容发生了变化,从而加载最新的内容。
  • 全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。子应用需要和基座共享cookie的话,条件还是比较苛刻的,需要主域名一致

在主应用中通过 <iframe> 嵌入子应用:

1
<iframe src="http://localhost:3000/react-app" width="100%" height="100%"></iframe>

实现步骤:

  1. 为每个子应用提供一个独立的 URL,并通过 iframe 嵌入。
  2. 可以通过 postMessage 来实现跨 iframe 的通信。

微前端架构的优缺点

优点:

  1. 技术栈无关:允许多个团队使用不同的前端技术栈开发子应用。
  2. 独立部署:每个子应用可以独立构建和部署,不影响其他子应用。
  3. 团队独立:每个团队可以独立管理自己的子应用,减少沟通和协作成本。
  4. 渐进升级:可以逐步将旧的单体应用迁移到微前端架构中,无需一次性重写整个应用。

缺点:

  1. 复杂性:需要解决子应用之间的依赖、共享资源、样式隔离等问题。
  2. 性能问题:如果多个子应用在一个页面中加载,可能会导致性能问题,如重复加载相同的依赖。
  3. 开发体验:虽然各个子应用独立,但在开发过程中需要额外的工具来管理开发环境、调试等。

微前端应用中的关键问题

  1. 子应用之间的通信:可以通过全局状态管理、window.postMessageCustomEvent 等来实现。
  2. 样式隔离:可以通过 CSS Module、Scoped CSS 或者像 Qiankun 提供的沙箱机制来处理。
  3. 性能优化:通过共享依赖、懒加载等手段来优化微前端的性能。

总结

微前端是一种强大的前端架构,可以解决大型项目中的开发、部署和维护问题。根据具体的需求,你可以选择不同的实现方案:

  • 如果你想要高度灵活性,可以选择 Single-SPA
  • 如果你想要快速上手并获得开箱即用的功能, Qiankun 是一个优秀的选择。
  • 如果你已经在使用 Webpack 5,并且需要模块共享功能,可以使用 Module Federation
  • 如果你只需要简单的隔离方案,或者快速实现,可以使用 Iframe-based 的方案。

每种技术方案都有它的应用场景和优缺点,学习和掌握这些工具后,你可以根据项目的具体需求灵活选择。