目前主流微前端技术方案的调研与实践

大家好,我是多喝热水。

最近我们公司编程导航网站需要去集成一些工具库,而这些工具又是一些独立存在的个体,我们在想如何能够以最低成本去接入这些工具的功能,因此我们调研了一些主流的微前端框架并进行了实践。

本文将深入探讨微前端应用的架构设计,分析社区中主流的微前端解决方案,并通过实际案例演示如何跑通一个微前端应用~

一、微前端应用的架构?

主应用

微前端需要一个主应用,它负责去调度不同的微应用,相当于是一个应用基座。

微应用

微应用可以有多个,可以是不同技术栈开发的项目,如 Vue.js、React.js、Solid.js 等。

二、社区微前端的解决方案

1、无界

腾讯在维护的,后起之秀,非常轻量,3k star,无痛接入 vite,对比下来个人比较喜欢的一种微前端方案。

无界的运行模式

此图为无界官方文档的运行模式图

跑通一个案例

1)第一步安装主应用基座,执行如下图命令:

2)第二步初始化package.json文件并基于 pnpm 配置 monorepo 架构(详细文档移步 从理解软/硬链接掌握PNPM),如下:

3)编写 pnpm-workspace.yaml 文件,如下:

arduino 复制代码
packages:
  - 'main' # main目录下的packge.json需要安装
  - 'packages/**' #packages目录下的所有项目的package.json需要安装

4)创建多个微应用,如下:

4)安装无界并将所有微应用关联起来

无界对于Nodejs的版本有要求,需要Nodejs < v18,参考此处文档无界

在主应用安装wujie-vue3(同时会把我们主/微应用所需要的所有依赖的都安装完),这个是作者封装的组件,可以减少用户配置的成本,如下:

此时我们的目录是这样的,node_modules中只保留最核心的包,一些辅助用的包,如Babel会被提升到最外层的node_modules,如下:

5)在主应用中注册无界组件,如下:

6)启动主应用和所有微应用

这里我们可以给最外层的package.json配置一下scripts脚本,避免每次都需要进入packages文件夹启动微应用,配置完成依次启动,如下:

7)启动后在主应用中引入对应的微应用

8)完成启动

总结

从跑通这个案例来看,无界的接入非常简单轻量,且完全支持 vite(这是其他微前端框架没有的)

简单了解一下原理

1)CSS 隔离:使用 shadowDOM 隔离

2)JS 隔离:使用一个空的 iFrame 隔离

3)多应用通讯:使用 Proxy

2、qiankun

该框架是蚂蚁在维护的,15k star,目前官方使用的是 webpack 作为构建工具,没有明确表示支持 vite,社区有 vite-plugin-qiankun 插件支持,但是配置起来还是有一定的心智负担,且可能存在不确定的问题,故此处使用webpack进行演示。

跑通一个案例

1)创建主应用(这次我们使用create-react-app来创建基于react的主应用)

2)配置monorepo架构(与无界中一致,这里不再赘述)

3)创建微应用(这里我们统一都使用 webpack 作为构建工具)

首先我们安装一下 vue/cli,如下:

再创建一个基于vue/cli的微应用,如下:

4)在主应用中注册微应用

js 复制代码
import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
  {
    name: 'vue app',
    entry: '//localhost:7100',
    container: '#vueapp',
    activeRule: '/vueapp',
  },
]);

start();

在主应用中配置微应用的显示位置,如下:

5)配置微应用,根据文档所述,我们需要在微应用的入口导出对应的生命周期钩子给到qiankun在合适的时机调用,且需要根据不同的运行时设置不同的publicPath,参考此处文档 项目实践 - qiankun

第一步,在微应用入口文件的同级目录下创建一个public-path.js文件,并写入如下内容:

js 复制代码
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

第二步,在微应用的package.json中加入如下声明,告诉 eslint 这个 webpack_public_path 全局变量时存在的,不要抛出错误,如下:

js 复制代码
"eslintConfig": {
  ...
    "globals": {
      "__webpack_public_path__": true
    },
  ...
  },

第三步,在微应用入口文件抛出生命周期钩子函数给到qiankun调用,如下:

ts 复制代码
import "./public-path";
import { createApp } from "vue";
import App from "./App.vue";

let instance = null;

function render(props = {}) {
  const { container } = props;
  createApp(App).mount(container ? container.querySelector("#app") : "#app");
}

// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {
  console.log("[vue] vue app bootstraped");
}
export async function mount(props) {
  console.log("[vue] props from main framework", props);
  render(props);
}
export async function unmount() {
  instance?.unmount();
  instance.$el.innerHTML = "";
  instance = null;
}

第四步,在vue.config.js文件中配置微应用的打包方式,并配置跨域【重要】

js 复制代码
const { defineConfig } = require("@vue/cli-service");
const { name } = require("./package");
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
  },
  configureWebpack: {
    output: {
      library: `${name}-[name]`,
      libraryTarget: "umd", // 把微应用打包成 umd 库格式
      chunkLoadingGlobal: `webpackJsonp_${name}`, // webpack 5 需要把 jsonpFunction 替换成 chunkLoadingGlobal
    },
  },
});

6)启动微应用和主应用

7)访问/vue-app 路由成功渲染微应用

总结

1)配置比较繁琐,个人感觉需要用户去配置的东西太多了,单从启动一个微前端应用来说,确实没有无界方便

2)暂时无法支持 vite 作为开发服务器,只能使用 webpack,接入相对比较重

3、两者实现原理对比(拓展)

参考文章:juejin.cn/post/722136...

无界 qiankun
应用加载机制 无需注册,直接将子应用注入主应用同域的iframe中运行 首先基于single-spa 注册子应用,然后通过import-html-entry获取子应用的相关资源并对这些资源进行加工,随后构造和执行一些生命周期中需要执行的方法,并返回一个函数,该函数的返回值是一个包括了子应用生命周期方法的对象
沙箱隔离机制 js通过iframe来隔离,css通过web component + shadowdom来隔离 js隔离机制 : SnapshotSandbox、LegacySandbox、ProxySandboxcss隔离机制:strictStyleIsolation、experimentalStyleIsolation
路由保持机制 浏览器的前进后退可以不作任何处理直接作用于子应用,通过监听iframe的路由变化可以将子应用的url同步到主应用 通过props实现全局路由数据的存储及共享
应用通信机制 提供多种通信方式:window.parent直接通信、props数据注入、去中心化EventBus通信机制 通过发布订阅模式来实现通信,状态和回调处理函数全局统一维护,全局状态发生变化时触发各个应用注册的回调函数执行,将新旧状态传递到所有应用

4、两者优缺点对比

优点 缺点
无界 1)具备qiankun的所有优点2)主应用使用成本及子应用适配成本低3)css沙箱和js沙箱都采用了原生隔离,无需担心污染问题4)支持路由保活和共享依赖5)具有强大插件系统,方便在运行时修改子应用代码 目前还比较新, 社区不够活跃
qiankun 1)能监听路由自动加载和卸载当前路由对应的子应用2)具有完备沙箱方案来隔离js和css3)支持静态资源预加载4)应用间通信简单 1)基于路由匹配,无法同时激活多个子应用,也不支持子应用保活2)改造成本较大,从 webpack、代码、路由等都要做一系列的适配3)css 沙箱无法绝对的隔离,js 沙箱在某些场景下执行性能下降 严重4)无法支持 vite 等 ES Module 脚本运行

三、对微前端的一些看法

什么时候需要用到它?

1)不同项目使用的技术栈不同,想将它们组合到一起

2)为了解决项目代码组织问题,而不是性能问题

一个使用场景

假如我有一个系统是用 jQuery + PHP 写的,但是它已经在线上稳定运行了好几年的时间,而现在需要在这个系统上加一些新需求,但是碍于技术栈过于老旧而导致开发效率低下,想使用新的技术栈来开发(如Vue.js、React.js),重构又浪费时间和人力,那这个时候就可以考虑接入微前端,把旧系统作为一个微应用,新的业务逻辑重开一个项目写(也是一个微应用),这样我们使用最小的成本提升了开发效率。

优点

1)老旧技术栈项目也能平滑迁移至新的技术栈,提升项目的可扩展性

2)主应用不限制接入应用的技术栈,微应用可以自主选择技术栈

3)独立部署,降低一个前端应用每次部署涉及的范围,一定程度上减少了项目风险

缺点

1)应用拆分的越小,架构就会变得复杂、维护成本就会变高

2)技术栈多样化,同时也意味着技术栈混乱(但一般也不会超过2种以上?)

四、其他补充

micro-app

star 5k,也是一个微前端框架,京东推出,micro-app 的接入和wujie的步骤一致,仅仅只是将 wujie-vue3 换成了 @micro-zoe/micro-app

如下所示:

js 复制代码
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
import microApp from "@micro-zoe/micro-app";

createApp(App).mount("#app");

microApp.start();

注意 :在主应用中如果子应用是采用vite接入,那么需要使用 iframe, 官方的解释

js 复制代码
<template>
  <micro-app url="http://localhost:5173" height="500px" name="vue-app" iframe></micro-app>
  <micro-app url="http://localhost:5174" height="500px" name="react-app" iframe></micro-app>
</template>

否则你可能会看到如下错误:

相关推荐
一颗花生米。2 小时前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
学习使我快乐012 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19952 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
勿语&3 小时前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
黄尚圈圈3 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水4 小时前
简洁之道 - React Hook Form
前端
正小安6 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch7 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光7 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   7 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发