微前端
一、简介
微前端的概念是由ThoughtWorks在2016年提出的,它借鉴了微服务的架构理念,核心在于将一个庞大的前端应用拆分成多个独立灵活的小型应用,同时可以解决一些iframe的潜在问题,通俗来说,就是在一个web应用中可以独立的运行另一个web应用。
微前端解决方案也给我们提供如下特性:
-
单个前端部分可独立开发、测试和部署;
-
无需重新构建即可添加、移除或替换单个前端部分;
-
不同的前端部分可使用不同的技术构建;
-
解决iframe硬隔离的一些问题
二、微前端实现
这里采用wujie,wujie是一个基于 Web Component 容器 + iframe 沙箱的微前端方案。能够完善的解决适配成本、样式隔离、运行性能、页面白屏、子应用通信、子应用保活、多应用激活、vite 框架支持、应用共享等问题。
三、应用
1. 安装wujie
主应用不限技术栈,只需引入 wujie、配置子应用路由并启动 wujie 即可。
wujie 针对 React 和 Vue 框架分别提供了 wujie-react wujie-vue2 wujie-vue3 依赖。
yarn add wujie-vue3
pnpm add wujie-vue3
npm i wujie-vue3
2. 在main.ts中挂载
import { createApp } from 'vue';
import WujieVue from 'wujie-vue3';
import App from './App.vue';
const app = createApp(App);
app.use(WujieVue).mount('#app');
3. 在apps文件夹下创建子应用
4. 在页面中应用
创建子应用
<template>
<WujieVue
width="100%"
height="100%"
name="sub-app"
:url="url"
:props="propsData"
/>
</template>
<script setup>
import { ref, reactive } from 'vue';
const url = ref('http://192.168.201.0:7300/'); // 子应用的url
const propsData = reactive({ // 传递参数
name: '章三',
age: '18',
});
// sync 路由同步
</script>
生命周期
name 为子应用唯一标识符,url 为子应用的路径地址,alive 设为 true 则是保活模式,after-mount 为子应用渲染后的生命周期钩子,无界的主要生命周期可以分为:
-
beforeLoad:子应用开始加载静态资源前触发
-
beforeMount:子应用渲染(调用window.__WUJIE_MOUNT)前触发
-
afterMount:子应用渲染(调用window.__WUJIE_MOUNT)后触发
-
beforeUnmount:子应用卸载(调用window.__WUJIE_UNMOUNT)前触发
-
afterUnmount: 子应用卸载(调用window.__WUJIE_UNMOUNT)后触发
-
activated:子应用保活模式下,进入时触发
-
deactivated:子应用保活模式下,离开时触发
运行模式:
-
保活模式:子应用的alive设置为true时进入保活模式,内部的数据和路由的状态不会随着页面切换而丢失。在保活模式下,子应用只会进行一次渲染,页面发生切换时承载子应用dom的webcomponent会保留在内存中,当子应用重新激活时无界会将内存中的webcomponent重新挂载到容器上。保活模式下改变 子应用的路由不会发生变化,需要采用通信的方式对子应用路由进行跳转
-
单例模式:子应用的alive为false且进行了生命周期改造时进入单例模式。子应用页面如果切走,会调用window.__WUJIE_UNMOUNT销毁子应用当前实例,子应用页面如果切换回来,会调用window.__WUJIE_MOUNT渲染子应用新的子应用实例,在单例式下,改变 url 子应用的路由会发生跳转到对应路由。如果主应用上有多个菜单栏用到了子应用的不同页面,在每个页面启动该子应用的时候将name设置为同一个,这样可以共享一个wujie实例,承载子应用js的iframe也实现了共享,不同页面子应用的url不同,切换这个子应用的过程相当于:销毁当前应用实例 => 同步新路由 => 创建新应用实例
-
重建模式:子应用既没有设置为保活模式,也没有进行生命周期的改造则进入了重建模式,每次页面切换不仅会销毁承载子应用 dom 的 webcomponent,还会销毁承载子应用js的iframe,相应的 wujie 实例和子应用实例都会被销毁。重建模式下改变 url 子应用的路由会跳转对应路由,但是在路由同步场景并且子应用的路由同步参数已经同步到主应用url上时则无法生效,因为改变url后会导致子应用销毁重新渲染,此时如果有同步参数则同步参数的优先级最高
路由同步
路由同步会将子应用路径的path+query+hash通过window.encodeURIComponent编码后挂载在主应用url的查询参数上,其中key值为子应用的 name。
开启路由同步后,刷新浏览器或者将url分享出去子应用的路由状态都不会丢失,当一个页面存在多个子应用时无界支持所有子应用路由同步,浏览器刷新、前进、后退子应用路由状态也都不会丢失
开启参数 sync
父子应用通信
- props 通信
主应用可以通过props注入数据和方法:
<WujieVue name="xxx" url="xxx" :props="{ data: xxx, methods: xxx }"></WujieVue>
子应用可以通过$wujie来获取:
const props = window.$wujie?.props; // {data: xxx, methods: xxx}
- window 通信
主应用调用子应用的全局数据
window.document.querySelector("iframe[name=子应用id]").contentWindow.xxx;
子应用调用主应用的全局数据
window.parent.xxx;
- eventBus 通信
无界提供一套去中心化的通信方案,主应用和子应用、子应用和子应用都可以通过这种方式方便的进行通信,
主应用使用方式:
// 如果使用wujie-vue
import WujieVue from "wujie-vue3";
const { bus } = WujieVue;
// 主应用监听事件
bus.$on("事件名字", function (arg1, arg2, ...) {});
// 主应用发送事件
bus.$emit("事件名字", arg1, arg2, ...);
// 主应用取消事件监听
bus.$off("事件名字", function (arg1, arg2, ...) {});
子应用使用方式:
// 子应用监听事件
window.$wujie?.bus.$on("事件名字", function (arg1, arg2, ...) {});
// 子应用发送事件
window.$wujie?.bus.$emit("事件名字", arg1, arg2, ...);
// 子应用取消事件监听
window.$wujie?.bus.$off("事件名字", function (arg1, arg2, ...) {});
四、总结
总的来说无界的优点还是很明显:
-
使用iframe作为天然的 js 沙箱,不会污染主应用环境
-
使用 Web Components 来隔离html、css,这是一个浏览器原生支持的组件封装技术,可以有效隔离元素之间的样式
-
提供了事件总线和props传参,弥补了iframe通信困难的缺点
-
子应用无需做任何改造,接入更简单
-
实现了多应用激活、应用保活功能
-
提供了vue和react组件的封装,开箱即用