Skip to content

微前端

微前端(Micro Frontends)是一种架构风格,它将大型前端应用程序分解为一组更小、更易于管理的独立前端应用程序。这些小型前端应用程序(通常称为“微应用”)可以独立开发、测试、部署和维护。

解决公司历史遗产代码兼容性问题

特点

  1. 独立性:每个微应用可以独立开发和部署,不受其他微应用的影响。

  2. 技术栈无关性:不同的微应用可以选择不同的技术栈(例如,React、Vue、Angular)。

  3. 可扩展性:新的微应用可以很容易地添加到系统中,以支持新功能或业务需求。

  4. 团队自治:每个微应用通常由一个独立的团队负责,团队可以自主选择开发工具、流程和发布计划。

  5. 共享生态:微应用之间可以共享一些公共的库、组件和服务,以保持一致性和减少重复工作。

  6. 接口驱动:微应用之间通过定义清晰的接口(如 API、Web Components)进行通信和数据交换。

  7. 独立运行:每个微应用可以作为一个独立的单元运行,也可以与其他微应用组合在一起。

解决方案

iframe

  • 优点:
    • 简单
    • 隔离
    • 无限制使用
  • 缺点:
    • 每次进入都要加载,状态不保留
    • 通信不方便(postMessage)
    • 无法资源共享,整个应用全量资源加载,加载太慢(没有办法复用)

模块联邦

模块联邦(Module Federation)是一种 Webpack v5 引入的先进技术,它允许你将多个 Webpack 应用组合成一个更大的应用。模块联邦的核心思想是让独立的 Webpack 应用能够互相加载对方的模块,从而实现应用之间的资源共享和通信。

  • 优点:
    • 可以在a系统中直接复用b系统的组件
    • 每个应用也是独立开发打包部署
    • webpack构建系统可以优化模块联邦应用的构建过程,提高效率
  • 缺点:
    • 得用同一种语言比如vue、或者react,如果跨语言调用组件会很麻烦

web components

  • 优点:
    • 技术栈无关:是浏览器原生组件
    • 独立开发
    • 应用隔离
  • 缺点:
    • 语法强制(强制使用某一种技术栈)

服务端拼接

配置nginx代理

ESM + importmap

ESM引用必须要用"/"或者"./"开头

浏览器原生支持

微前端框架

single-spa

  • 加载微应用(自己实现加载方法)
  • 管理微应用的状态(初始化、挂载、卸载)

qiankun

基于single-spa实现的,孵化自蚂蚁金融科技基于微前端架构的云产品统一接入平台。

访问qiankun

实战应用

前段时间紧急演示搞了一下嵌入式,基本上把各种疑难杂症算是解决了一圈,所以还是要记录一下。当然有人说不如无界wujie好用,所以讲完qiankun马上讲wujie

  • 这里使用vite vue3架构来实现qiankun嵌入,将一个子系统完全嵌入主系统,并且实现单点登录。
主程序
  1. 首先安装qiankun
shell
pnpm install qiankun
  1. 然后新建一个qinakun的文件夹,里面放一个index.ts,当然你丢到哪里都无所谓,比如我其实在util文件夹里面放了一个qiankun.ts

内容如下:

ts
import { runAfterFirstMounted } from 'qiankun'

runAfterFirstMounted(() => {
    console.log('qiankun开机')
})

注意:我们这里演示的是缓存写法,所以不在全局注册所有的子系统。

然后在main.ts中引用这个文件。

  1. 因为子系统要嵌入到某个页面的内部,所以我们提前定义好页面组件,并且配置好路由。

这个子组件SubSystem.vue是这么写的:

vue
<template>
    <div class="son-system" v-loading="loading">
        <div id="subSystem" style="height:100%"></div>
    </div>
</template>
<script setup>
import { start, loadMicroApp } from 'qiankun'

const isDevelopment = import.meta.env.MODE === 'development'
const loading = ref(true)

// 获取子系统传递的消息
actions.onGlobalStateChange((state, prev) => {
    // state: 变更后的状态; prev 变更前的状态
    setTimeout(() => {
        loading.value = false;
    }, 500)
});
onMounted(() => {
    if (!window.qiankunStarted) {
        // 这里写你子系统的地址
        const url = (isDevelopment ? "http://localhost:5174" : "/subSystem");
        loadMicroApp({
            name: "subSystem", //子应用名称
            entry: url, //子应用地址
            container: "#subSystem", //子应用要挂载的节点,和vue.$mount("#app")类似;
        });
        window.qiankunStarted = true
        start();
    }
})
</script>
<style lang='scss' scoped>
.son-system {
    width: 100%;
    height: 600px;
}
</style>
子程序
  1. 首先给子系统安装一下vite-plugin-qiankun,因为vite不被qiankun支持,但是有大佬做了插件兼容了。

  2. 然后先去vite.config.ts中,做一些配置如下:

ts
import qiankun from 'vite-plugin-qiankun'
export default ({ command, mode }: ConfigEnv): UserConfigExport => defineConfig({
  plugins: [
    qiankun('subSystem', {  // 子应用名称一致
      useDevMode: true
    })
  ]
})
  1. 接着去main.ts中,做一些配置:
ts
import { renderWithQiankun, qiankunWindow, QiankunProps } from 'vite-plugin-qiankun/dist/helper'

const render = (props: QiankunProps = {}) => {
    const { container } = props
    const appEle: string | Element = container?.querySelector('#app') || '#app' // 避免 id 重复导致微应用挂载失败

    // 注意把原来的挂载提进来
    app.mount(appEle)   
}

const initQianKun = () => {
    renderWithQiankun({
        bootstrap() {
            console.log('我是微应用:bootstrap')
        },
        mount(props) { // 获取主应用传入数据
            console.log('我是微应用:mount', props)
            props.setGlobalState({
                msg:"i am OK"
            });
            render(props)
        },
        unmount(props) {
            console.log('我是微应用:unmount', props)
        },
        update(props) {
            console.log('我是微应用:update', props)
        },
    })
}

qiankunWindow.__POWERED_BY_QIANKUN__ ? initQianKun() : render() // 判断是否使用 qiankun ,保证项目可以独立运行
  1. 再去router中该路由,如下:
ts
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
const router = createRouter({
  history: createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__
    ? '/subSystem'
    : import.meta.env.BASE_URL),  
})
  1. 最重要的来了,去主程序里面拦截路由:
js
router.beforeEach((to, from, next) => {
  // 主程序清除由于子程序路由导致的变化
  if (to.href.includes('/subSystem/')) {
    next({ path: '/subSystem' });
    return
  }
  ...
  ...
  ...
}
  1. 其他问题
  • 缓存问题
    • 之所以用手动挂载就是为了解决缓存问题
    • 需要主系统和子系统分别设置keepalive
  • 单点登录
    • 主系统登录的时候调用子系统的登录方法并按照对应规则存储

无界wujie

链接

官网东西很好,不做拓展了

主应用

  • 找个页面随便写写代码,就结束了,如果都配置了keepalive的话,缓存也可以正常使用。-
vue
<template>
    <div class="son-system">
        <WujieVue width="100%" height="100%" name="xxx" url="http://127.0.0.1:5176/sonApp/" :sync="true" 
            :props="{}" :beforeLoad="beforeLoad" :beforeMount="beforeMount" :afterMount="afterMount"
            :beforeUnmount="beforeUnmount" :afterUnmount="afterUnmount"></WujieVue>
    </div>
</template>
<script setup>
import WujieVue from "wujie-vue3";
const beforeLoad = (e) => {
    console.log("beforeLoad", e)
}
const beforeMount = (e) => {
    console.log("beforeMount", e)
}
const afterMount = (e) => {
    console.log("afterMount", e)
}
const beforeUnmount = (e) => {
    console.log("beforeUnmount", e)
}
const afterUnmount = (e) => {
    console.log("afterUnmount", e)
}
</script>
<style lang='scss' scoped>
.son-system {
    width: 100%;
    height: 600px;
    margin: auto;
}
</style>
  • 设计其他的通信等可以去官网找api,这里不做赘述。
  • 至于单点登录很简单,不做赘述,用通讯就行

整个系统按理上传到了 这里 ,自行下载查看

优势

微前端与iframe相比优势在哪:

  1. 搜索引擎优化(SEO)对微前端更友好,因为dom元素存在于document中
  2. iframe 会为每个内嵌页面创建一个独立的文档和全局作用域,这可能导致额外的资源消耗和性能问题
  3. iframe 的集成方式较为固定,限制了页面间的交互和通信
  4. iframe 内嵌的页面通常不支持SPA的路由方式,微前端架构可以与SPA路由无缝集成,提供无刷新的页面导航体验
  5. iframe 的缓存只能通过隐藏的形式,系统多的时候内存消耗过大
  6. 从6开始主要说wujie框架的优势,preload,空闲时候可以预加载
  7. 额外提供了插件的能力,例如修改子应用所有的aaa为bbb,按需加载js,替换js,提前执行自定义js,延后执行js
  8. css也是同理

micro app

补充一下微前端技术,这个技术来自于京东,这个跟wujie有点像,上 链接

缺点:

  • 1.0之前不支持vite,1.0之后支持
  • 子应用和主应用必须相同的路由模式,要么同时hash模式,要么同时history模式

鄂ICP备2024055897号