微前端从源码看无界:为何说它是 qiankun 的 “轻量化替代方案”

背景

前面我们已经从源码深入研究过「qiankun 微前端框架」了,感兴趣的小伙伴可以去看看《微前端 qiankun@2.10.5 源码分析(二)》,今天我们来研究另外一个比较牛的微前端框架「无界」。

简介

无界微前端(Wujie)是一款由腾讯团队开源的微前端框架,专注于解决复杂前端应用的模块化、子应用独立开发和部署等问题。它通过 Web ComponentsShadow DOM 技术实现子应用的样式隔离脚本沙箱,同时支持跨技术栈的无缝集成(如 React、Vue、Angular 等)。

具体的特性跟使用就不详细说明了,小伙伴们可以自己查阅「无界」开发文档,今天我们主要是从源码角度去分析下无界微前端框架。

开始

我们直接去 github 官网拖一份目前最新的 Wujie 源码@1.0.28,然后进入项目根目录按装依赖:

cd wujie && pnpm install

在这里插入图片描述

注意:Node 版本要指定 <=16。

先来一张 wujie 框架的流程图,这样就不会在庞大的源码中感到迷茫了:
在这里插入图片描述

我们先在项目根目录安装依赖并执行 pnpm start 命令来启动一下项目:

pnpm install && pnpm start

启动完成后:
在这里插入图片描述

setupApp 函数

找到主应用 examples/main-vue/src/main.js:

import "whatwg-fetch"; // fetch polyfill
import "custom-event-polyfill";

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import WujieVue from "wujie-vue2";
import hostMap from "./hostMap";
import credentialsFetch from "./fetch";
import Switch from "ant-design-vue/es/switch";
import Tooltip from "ant-design-vue/es/tooltip";
import button from "ant-design-vue/es/button/index";
import Icon from "ant-design-vue/es/icon/index";
import "ant-design-vue/es/button/style/index.css";
import "ant-design-vue/es/style/index.css";
import "ant-design-vue/es/switch/style/index.css";
import "ant-design-vue/es/tooltip/style/index.css";
import "ant-design-vue/es/icon/style/index.css";
import lifecycles from "./lifecycle";
// import plugins from "./plugin";

const isProduction = process.env.NODE_ENV === "production";
const {
    setupApp, bus } = WujieVue;
Vue.use(WujieVue).use(Switch).use(Tooltip).use(button).use(Icon);

Vue.config.productionTip = false;

bus.$on("click", (msg) => window.alert(msg));

// 在 xxx-sub 路由下子应用将激活路由同步给主应用,主应用跳转对应路由高亮菜单栏
bus.$on("sub-route-change", (name, path) => {
   
  const mainName = `${
     name}-sub`;
  const mainPath = `/${
     name}-sub${
     path}`;
  const currentName = router.currentRoute.name;
  const currentPath = router.currentRoute.path;
  if (mainName === currentName && mainPath !== currentPath) {
   
    router.push({
    path: mainPath });
  }
});

const degrade = window.localStorage.getItem("degrade") === "true" || !window.Proxy || !window.CustomElementRegistry;
const props = {
   
  jump: (name) => {
   
    router.push({
    name });
  },
};
/**
 * 大部分业务无需设置 attrs
 * 此处修正 iframe 的 src,是防止github pages csp报错
 * 因为默认是只有 host+port,没有携带路径
 */
const attrs = isProduction ? {
    src: hostMap("//localhost:8000/") } : {
   };
/**
 * 配置应用,主要是设置默认配置
 * preloadApp、startApp的配置会基于这个配置做覆盖
 */
//...
setupApp({
   
  name: "vue2",
  url: hostMap("//localhost:7200/"),
  attrs,
  exec: true,
  props,
  fetch: credentialsFetch,
  degrade,
  alive: false,
  ...lifecycles,
});
//...
new Vue({
   
  router,
  render: (h) => h(App),
}).$mount("#app");

可以看到,用 setupApp 方法注册了一个子应用“vue2”,并提供了入口文件“//localhost:7200/”,我们找到 setupApp 方法,在 packages/wujie-core/src/index.ts 文件的第 179 行:

/**
 * setupApp 参数
 */
 type baseOptions = {
  /** 唯一性用户必须保证 */
  name: string;
  /** 需要渲染的url */
  url: string;
  /** 需要渲染的html, 如果已有则无需从url请求 */
  html?: string;
  /** 代码替换钩子 */
  replace?: (code: string) => string;
  /** 自定义fetch */
  fetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
  /** 注入给子应用的属性 */
  props?: { [key: string]: any };
  /** 自定义运行iframe的属性 */
  attrs?: { [key: string]: any };
  /** 自定义降级渲染iframe的属性 */
  degradeAttrs?: { [key: string]: any };
  /** 子应用采用fiber模式执行 */
  fiber?: boolean;
  /** 子应用保活,state不会丢失 */
  alive?: boolean;
  /** 子应用采用降级iframe方案 */
  degrade?: boolean;
  /** 子应用插件 */
  plugins?: Array<plugin>;
  /** 子应用生命周期 */
  beforeLoad?: lifecycle;
  beforeMount?: lifecycle;
  afterMount?: lifecycle;
  beforeUnmount?: lifecycle;
  afterUnmount?: lifecycle;
  activated?: lifecycle;
  deactivated?: lifecycle;
  loadError?: loadErrorHandler;
};
export function setupApp(options: cacheOptions): void {
  if (options.name) addSandboxCacheWithOptions(options.name, options);
}
//...
export function addSandboxCacheWithOptions(id: string, options: cacheOptions): void {
  const wujieCache = idToSandboxCacheMap.get(id);
  if (wujieCache) idToSandboxCacheMap.set(id, { ...wujieCache, options });
  else idToSandboxCacheMap.set(id, { options });
}

可以看到,其实 setupApp 方法就是把当前子应用的一些参数信息先缓存起来,后面创建、渲染的时候再去取。

当我们浏览器输入“http://localhost:8000/vue2-sub/home” 地址的时候,首先我们的主项目“main-vue” 中的 vue-router 会匹配对应的渲染页面:

import Vue from "vue";
import VueRouter from "vue-router";
import Vue2Sub from "../views/Vue2-sub.vue";
//...

const basename = process.env.NODE_ENV === "production" ? "/demo-main-vue/" : "";

Vue.use(VueRouter);

const routes = [
  //..
  {
   
    path: "/vue2-sub/:path",
    name: "vue2-sub",
    component: Vue2Sub,
  },
  //...
];

const router = new VueRouter(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值