无界-极致的微前端框架

3,755 阅读5分钟

什么是微前端

简单的理解,微前端就是把一个应用当成vue组件嵌入到另一个应用。一个前端项目无非就是htmlcssjs三大件,微前端框架不仅要将这三大件引入到主应用,还要做好隔离防止污染(比如两个应用存在重复的class,那么就会照成样式污染,如果window下挂载了同样的方法,方法就会被覆盖)

iframe方案

优点

  1. iframeweb应用完美隔离,无论是domcss还是js都完全的隔离了起来
  2. 使用简单,一个iframe标签加一个src属性即可将子应用嵌入进来的

缺点

  1. dom隔离太严重,子应用的dom无法突破iframe的限制,比如一个fixed定位的元素也只能在iframe区域展示
  2. 路由无法保活,每次浏览器刷新iframe里的页面就会重置
  3. 通信困难,全局上下文完全隔离

qiankun方案

优点

  1. qiankun 使用 HTML entry 及 css沙箱 实现了 dom 和样式隔离
  2. 实现js隔离,并提供了父子通信及全局状态的方法
  3. 实现了资源预加载能力,预先子应用htmljscss资源缓存下来,加快子应用的打开速度

缺点

  1. 子应用基于路由匹配,父子应用共用一个路由,缺乏灵活性,无法实现多应用激活
  2. 需要改造子应用,改造成本大
  3. 开启严格模式后会出现各种问题

其中共用一个路由是让我无法接受的,比如qiankun子应用在layout中,主应用切换页面时还要保持子应用匹配的部分不动。我认为的微前端就应该把子应用当成组件一样嵌入到主应用,除了父子通信外不做其他耦合。直到无界横空出世,我认为这就是我梦寐以求的微前端框架。

无界方案

无界是腾讯团队开源的微前端框架,是一款基于 Web Components + iframe 微前端框架,具备成本低、速度快、原生隔离、功能强等一系列优点

优点

  1. 使用iframe作为天然的 js 沙箱,不会污染主应用环境
  2. 使用 Web Components 来隔离htmlcss,这是一个浏览器原生支持的组件封装技术,可以有效隔离元素之间的样式
  3. 提供了事件总线和props传参,弥补了iframe通信困难的缺点
  4. 子应用无需做任何改造,接入更简单
  5. 实现了多应用激活、应用保活功能
  6. 提供了vue和react组件的封装,开箱即用

无界用起来感觉和iframe差不多,js沙箱直接使用iframe,dom和样式使用Web Components,巧妙的避开了iframe的缺点 感觉无界就是个缝合怪,这很Tencent

缺点

  1. Web Components兼容性有限,在不支持Web Components的浏览器中,无界提供了降级处理,html和css的隔离会使用iframe替代,降级后的无界其实就是个iframe了

其实无界的适配问题无伤大雅,就算有些浏览器不支持Web Components,那我们就当是用了个iframe就好了。毕竟qiankun和iframe相比,我还是觉得iframe更友好一点,更何况是拥有事件总线的iframe呢。

快速上手

官方文档:wujie-micro.github.io/doc/

github:github.com/Tencent/wuj…

安装

npm install wujie -S

使用

<template>
    <div id="container"></div>
</template>
import { setupApp, preloadApp, startApp, destroyApp } from "wujie";
import { onMounted } from "vue";

// `setupApp`设置子应用默认属性,非必须。startApp、preloadApp会从这里获取子应用默认属性,如果有相同的属性则会直接覆盖
setupApp({ name: "wjapp", url: "子应用路径", el: "#container" });

// 预加载可以极大的提升子应用首次打开速度
// 由于setupApp已经定义好了默认配置,所以preloadApp只需要指定name
preloadApp({name: "wjapp"})

onMounted(()=>{
    // 启动子应用 异步返回 destroy 函数,可以销毁子应用
    // 可以只使用startApp来启动一个子应用,setupApp、preloadApp都是非必需的
    startApp({name: "wjapp"})
})

onBeforeUnmount(()=>{
    // 主动销毁子应用,承载子应用的`iframe`和`shadowRoot`都会被销毁,无界实例也会被销毁,相当于所有的缓存都被清空,除非后续不会再使用子应用,否则都不应该主动销毁。
    // 参数为子应用`name`
    destroyApp("wjapp")
})

setupApp配置参数

type startOption { 
    /** 唯一性用户必须保证 */ 
    name: string; 
    /** 需要渲染的url */ 
    url: string; 
    /** 渲染的容器 */ 
    el: HTMLElement | string; 
    /** 子应用加载时loading元素 */ 
    loading?: HTMLElement; 
    /** 路由同步开关, false刷新无效,但是前进后退依然有效 */ 
    sync?: boolean; 
    /** 子应用短路径替换,路由同步时生效 */ 
    prefix?: { [key: string]: string }; 
    /** 子应用保活模式,state不会丢失 */ 
    alive?: boolean; 
    /** 注入给子应用的数据 */ 
    props?: { [key: string]: any }; 
    /** js采用fiber模式执行 */ 
    fiber?: boolean; 
    /** 子应用采用降级iframe方案 */ 
    degrade?: boolean; 
    /** 自定义iframe属性 */ 
    attrs?: { [key: string]: any }; 
    /** 代码替换钩子 */ 
    replace?: (codeText: string) => string; 
    /** 自定义fetch,资源和接口 */ 
    fetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>; 
    /** 子应插件 */ 
    plugins: Array<plugin>; 
    /** 子应用生命周期 */ 
    beforeLoad?: lifecycle; 
    /** 没有做生命周期改造的子应用不会调用 */ 
    beforeMount?: lifecycle; 
    afterMount?: lifecycle; 
    beforeUnmount?: lifecycle; 
    afterUnmount?: lifecycle; 
    /** 非保活应用不会调用 */ 
    activated?: lifecycle; 
    deactivated?: lifecycle; 
    /** 子应用资源加载失败后调用 */ 
    loadError?: loadErrorHandler 
};

通信 bus

type callback = (...args: Array<any>) => any; 

export declare class EventBus { 
    private id; 
    private eventObj; 
    constructor(id: string); 
    $on(event: string, fn: callback): EventBus; 
    /** 任何$emit都会导致监听函数触发,第一个参数为事件名,后续的参数为$emit的参数 */
    $onAll(fn: (event: string, ...args: Array<any>) => any): EventBus;
    $once(event: string, fn: callback): void; 
    $off(event: string, fn: callback): EventBus; 
    $offAll(fn: callback): EventBus; 
    $emit(event: string, ...args: Array<any>): EventBus; 
    $clear(): EventBus; 
}

主应用

import { bus } from "wujie";

// 主应用监听事件 
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, ...) {});

无界还提供了vue和react的组件封装,参考官网

wujie-micro.github.io/doc/pack/