微前端框架选型以及实现

主应用地址: cloudbase-100032138116.coding.net/p/applet-fe...

创建子应用脚手架:cloudbase-100032138116.coding.net/p/applet-fe...

微前端架构设计:www.garfishjs.org/blog

微前端框架对比

框架 介绍 JS 沙箱 样式隔离 预加载 路由同步 数据通信 优点 缺点 背景
single-spa 最早的微前端框架 支持 支持 不支持 不支持 不支持 - 需要自己去加载子应用- 需要自己设置js隔离和css隔离- 无法预加载
Garfish 基于 SPA 的微前端架构 支持 支持 支持 支持 支持 和 qiankun 的 api 相似,改造成本低。没有基于 single-spa,而是重新实现的,项目的灵活性高,解决了 qiankun 的一些痛点。 字节
qiankun 基于 single-spa 的微前端实现库 支持 支持 支持 支持 支持 使用人数最多,沉淀了很多踩坑经验。 - 无法支持 vite 等 ESM 脚本运行- 样式隔离不够完美 - strictStyleIsolation: true1. 原理是利用 webComponent 的 shadowDOM 实现 可能会影响React事件、弹框样式丢失 - experimentalStyleIsolation: true1. 原理类似于 vue 的 scope-css 1. 目前性能较差,处于实验阶段 阿里
无界 基于 WebComponent 容器 + iframe 沙箱 支持 支持 支持 支持 支持 - 社区不太活跃,解决 bug 的速度较慢。- 调查了一下,看起来坑也挺多的,例子- 对有富文本的项目不友好。复杂的 iframe 到 WebComponent 的代理机制,导致市面上大部分富文本编辑器都无法在无界中完好运行。- 长期维护性一般 腾讯
MicroApp 基于 WebComponent 容器 + iframe 沙箱 支持 支持 支持 支持 支持 内置了两种 JS 沙箱。1. with 代理沙箱 1. 类 qiankun 的 with 代理沙箱,据说相比 qiankun 性能高点1. iframe 沙箱 1. 用于兼容 vite 场景 - 如果是 iframe 沙箱 + WebComponent,痛点和无界类似。 京东
EMP 基于Rspack、 Module Federation 支持 支持 支持 支持 支持 - 目前 Rspack 和 Module Federation 的社区沉淀较少 欢聚时代

总结

每个框架都存在一些问题。我们之前的框架是用的 qiankun,结合我们的业务场景,选了 Garfish。

Garfish 的开发团队,是 Web Infra(字节跳动的网络基础设施团队),该团队在前端开源中做了不少贡献,还是有一定影响力的。

有兴趣的可以看下 Module Federation,也是一种微前端架构(类似于服务端的微服务),感觉以后会成为微前端的主流方案。

Module Federation 的作者是 Webpack 的团队成员,也是 Web Infra 组织和 Rspack的团队成员。Vite 的下一个大版本也会和 web-infra 团队合作,共建 Module Federation。

Vue & Vite:现状与未来 - 尤雨溪

Garfish

快速开始

主应用

javascript 复制代码
// main.tsx
import Garfish from 'garfish'
import microFeApps from '@/config/micro-fe-apps'
import { GarfishCssScope } from '@garfish/css-scope'

Garfish.run({
  basename: '/',
  plugins: [GarfishCssScope({ fixBodyGetter: true })],
  props: {
    msg: 'hello',
  },
  apps: microFeApps,
  beforeLoad(appInfo) {
    console.log('子应用开始加载', appInfo.name)
  },
  afterLoad(appInfo) {
    console.log('子应用加载完成', appInfo.name)
  },
})

createRoot(document.getElementById('root')!).render(<App />)

// src/config/micro-fe-apps.ts
const microFeApps: MicroFeApps = [
  {
    name: 'mp-sub-react-demo/*',
    activeWhen: '/mp-sub-react-demo',
    domGetter: '#mp-sub-react-demo',
    // 子应用开发环境
    entry: 'http://localhost:8081',
    // 子应用生产环境
    // entry: 'https://res-sit.wandacm.com.cn/qianfan-static/demo/20240408164058/',
    cache: true,
  },
]

// src/config/routes.tsx
const routes = [
  {
    path: '/',
    element: <Layout />,
    errorElement: <ErrorPage />,
    children: [
      {
        path: 'mp-sub-react-demo/*',
        element: <div id="mp-sub-react-demo" className="garfish-sub" />,
        name: 'mp-sub-react-demo',
      },
    ],
  },
]

子应用

javascript 复制代码
// src/main.tsx
import RootComponent from '@/Root'
import { reactBridge } from '@garfish/bridge-react-v18'

export const provider = reactBridge({
  el: '#root',
  rootComponent: RootComponent,
})

/** 这能够让子应用独立运行起来,以保证后续子应用能脱离主应用独立运行,方便调试、开发 **/
if (!window.__GARFISH__) {
  createRoot(document.getElementById('root')!).render(
    // @ts-expect-error
    <RootComponent basename="/" />,
  )
}

// src/Root.tsx
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { type PropsInfo } from '@garfish/bridge-react-v18'

function RootComponent(appInfo: PropsInfo) {
  return (
    <RouterProvider
      router={createBrowserRouter(routes, {
        basename: appInfo.basename,
      })}
    />
  )
}

// src/config/routes.tsx
const routes = [
  {
    path: '/',
    element: <Layout />,
    errorElement: <ErrorPage />,
    children: [
      {
        index: true,
        element: <Home />,
      },
      {
        path: 'test',
        element: <Test />,
      },
    ],
  }
]

Webpack 配置

arduino 复制代码
const isDev = process.env.NODE_ENV === 'development'
const port = 8081

export default {
  mode: process.env.NODE_ENV,
  entry: path.join(srcDir, 'main.tsx'),
  output: {
    clean: !isDev,
    // 需要配置成 umd 规范
    libraryTarget: 'umd',
    // 修改不规范的代码格式,避免逃逸沙箱
    globalObject: 'window',
    // 保证子应用的资源路径变为绝对路径
    publicPath: isDev
      ? `http://localhost:${port}/`
      : `//res-sit.wandacm.com.cn/qianfan-static/demo/20240408164058/`,
  },
  devServer: {
    open: true,
    host: 'localhost',
    port,
    historyApiFallback: true,
    headers: {
      // 保证子应用的资源支持跨域,在上线后需要保证子应用的资源在主应用的环境中加载不会存在跨域问题(**也需要限制范围注意安全问题**)
      'Access-Control-Allow-Origin': '*',
    },
  },
  // 生产环境下,将 react 和 react-dom 作为外部依赖,由主应用提供
  externals: isDev
    ? undefined
    : {
        react: 'React',
        'react-dom': 'ReactDOM',
      },
}

如何接入一个子应用

  1. 创建子应用
lua 复制代码
pnpm create mp-sub
  1. 在主应用中注册子应用
arduino 复制代码
// src/config/micro-fe-apps.ts
const microFeApps: MicroFeApps = [
  // ...
  {
    name: 'mp-sub-test',
    activeWhen: '/mp-sub-test',
    domGetter: '#mp-sub-test',
    // 子应用开发环境
    entry: 'http://localhost:8081',
    // 子应用生产环境
    // entry: 'https://res-sit.wandacm.com.cn/qianfan-static/demo/20240408164058/',
    cache: true,
  },
]
  1. 在主应用的路由中添加子应用的根路由
javascript 复制代码
// src/config/routes.tsx
const routes = [
    // ...
    {
        path: 'mp-sub-test/*',
        element: <div id="mp-sub-test" className="garfish-sub" />,
        name: 'mp-sub-test',
    },
 ]

使用过程中遇到的问题

Vite 子应用开发环境报错 TypeError: Cannot read properties of undefined (reading 'GARFISH_EXPORTS')

TL;DR:当子应用为 ESModule 并且开启 vm 沙箱时 会出现错误。解决办法:子应用最好用 Webpack

因为 Vite 开发环境会进行依赖预构建,Vite 的开发服务器将所有代码视为原生 ES 模块。而 Garfish 支持 esModule时,需要关掉 vm 沙箱或者为快照沙箱时,才能够使用。关闭 vm 沙箱后,不支持主子应用隔离。

如果需要在 vm 沙箱下开启 ESModule 的能力,可以使用 @garfish/es-module 插件。但会带来严重的首屏性能问题。

详情:www.garfishjs.org/issues/#esm...

相关推荐
web1478621072333 分钟前
C# .Net Web 路由相关配置
前端·c#·.net
m0_7482478034 分钟前
Flutter Intl包使用指南:实现国际化和本地化
前端·javascript·flutter
飞的肖37 分钟前
前端使用 Element Plus架构vue3.0实现图片拖拉拽,后等比压缩,上传到Spring Boot后端
前端·spring boot·架构
青灯文案11 小时前
前端 HTTP 请求由 Nginx 反向代理和 API 网关到后端服务的流程
前端·nginx·http
m0_748254881 小时前
DataX3.0+DataX-Web部署分布式可视化ETL系统
前端·分布式·etl
小屁不止是运维1 小时前
麒麟操作系统服务架构保姆级教程(五)NGINX中间件详解
linux·运维·服务器·nginx·中间件·架构
ZJ_.1 小时前
WPSJS:让 WPS 办公与 JavaScript 完美联动
开发语言·前端·javascript·vscode·ecmascript·wps
GIS开发特训营1 小时前
Vue零基础教程|从前端框架到GIS开发系列课程(七)响应式系统介绍
前端·vue.js·前端框架·gis开发·webgis·三维gis
Cachel wood2 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
学代码的小前端2 小时前
0基础学前端-----CSS DAY9
前端·css