需求背景

项目中需要接入PPT编辑器的功能,最终的解决方案是接入开源的PPT编辑器:PPTist,这种接入是不可能把代码完全融合进来的,那些需要做的调整太多,我们要做的是以微服务的形式,将功能引入到主站内。

常用的解决方案有两种:

iframe

为什么不用 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 方案。

微前端

微前端架构具备以下几个核心价值:

  • 技术栈无关

主框架不限制接入应用的技术栈,微应用具备完全自主权

  • 独立开发、独立部署

微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新

  • 增量升级

在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略

  • 独立运行时

每个微应用之间状态隔离,运行时状态不共享

实现细节

本次案例主应用和微应用技术栈均为:vue3X+vite6X,且需要注意的是,主应用不是传统意义上的基座,而是一个真实的应用,这是有本质区别的。

通用的微前端方案可以参考如下项目:https://github.com/kakajun/qiankun-vite-test

主应用

首先安装qiankun

pnpm install qiankun

具体策略是:在主应用中创建一个页面来装载微应用,首先要做的就是在router新建一个页面

  {
    path: '/ppt',
    name: 'ppt',
    component: () => import('@/views/ppt/index.vue'),
  },

然后在页面中添加一个容器来存放微应用,容器的ID是指定的:

<template>
  <!-- 容器的id。微应用配置的时候需要 -->
  <div id="sub-container"></div>
</template>

然后是注册微应用,需要注意的是:activeRule一定要是和微应用匹配的,否则会报错

registerMicroApps([
      {
        container: '#sub-container',
        name: 'ppt',
        entry: '//localhost:5188',
        activeRule: '/ppt',
      },
    ])

整体代码:

<template>
  <!-- 容器的id。微应用配置的时候需要 -->
  <div id="sub-container"></div>
</template>

<script setup lang="ts">
import { onMounted } from 'vue'
import { start } from 'qiankun'
import { registerMicroApps } from 'qiankun'

// 引入qiankun
const registerApps = () => {
  try {
    registerMicroApps([
      {
        container: '#sub-container',
        name: 'ppt',
        entry: '//localhost:5188',
        activeRule: '/ppt',
      },
    ])
  } catch (err) {
    console.log(err)
  }
}

onMounted(() => {
  registerApps()
  // 开启qiankun
  start({
    // sandbox: {
    //   // sandbox: { strictStyleIsolation: true } // 严格隔离
    //   // experimentalStyleIsolation: true // 样式隔离
    // }
  })
})
</script>

微应用

首先安装 vite-plugin-qiankun

pnpm install vite-plugin-qiankun

在 vite.config.ts 中安装插件

import qiankun from 'vite-plugin-qiankun';

export default {
  // 这里的 'myMicroAppName' 是子应用名,主应用注册时AppName需保持一致
  plugins: [qiankun('myMicroAppName')],
  // 生产环境需要指定运行域名作为base
  base: 'http://xxx.com/'
}

在入口文件里面写入乾坤的生命周期配置

// main.ts
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';

// some code
renderWithQiankun({
  mount(props) {
    console.log('mount');
    render(props);
  },
  bootstrap() {
    console.log('bootstrap');
  },
  unmount(props: any) {
    console.log('unmount');
    const { container } = props;
    const mountRoot = container?.querySelector('#root');
    ReactDOM.unmountComponentAtNode(
      mountRoot || document.querySelector('#root'),
    );
  },
});

if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  render({});
}

dev下作为子应用调试

qiankun('viteapp', {
      useDevMode
})