架构设计

前端整体使用的是 garfish 的微前端的实现方案来做的,以及主要技术栈是 react 和 vue 混合开发实现

通过 nodejs 运行时来实现对应的解决 garfish 在开发实践过程中遇到的痛点:比如自定义 app-registry/dev-kit 等子包来协助自己开发实践,解决运行时的一些问题吧

使用的是开发环境的配置包括:npmrc 来限制npm的一些默认行为,nvmrc限制npm使用的版本,editorconfig来统一不同编辑器的格式问题等等

garfish 微前端框架

官网阅读总结

概念:微前端是一种类似于微服务的一个开发架构和思想,核心是通过类似于微服务的架构模式来实现对应的搭建起一些额外的东西吧,核心解决的问题是将整体 APP 实现颗粒度拆分,每个功能实现单独维护,以及在主APP中聚合的开发模式吧

image.png

先来看一下官方给的案例吧

  • 这是 garfish 微前端的主应用 main app 呐,主要负责的事情是进行注册子应用并且运行呐

  • useEffect 来处理子应用加载的副作用

    • 依赖数组为空的时候,此时咱们的执行是只会执行一次呢:类似于 Vue 的 onMount

    • 依赖数组有依赖项的时候,只要依赖发生了变化,那么组件重新渲染执行

    • 依赖数组为 undefined的时候,每次组件渲染都执行呢

import React, { useEffect, Suspense } from 'react';
import Garfish from 'garfish';

const App = () => {
  useEffect(async () => {
    // docs: https://garfish.top/
    const app = await Garfish.loadApp('app2', {
      entry: 'http://localhost:3002',
      basename: '/',
      domGetter: '#container',
      sandbox: {
        fixBaseUrl: true,
      },
      props: {
        msg: 'hello world',
      },
    });
    await app.mount();
  });

  return (
    <div
      style={{
        margin: '10px',
        padding: '10px',
        textAlign: 'center',
        backgroundColor: 'greenyellow',
      }}
    >
      <h1>Main App</h1>
      <div id="container"></div>
    </div>
  );
};

export default App;

再来看看最终的效果是什么

核心就是字符串的实践吧,动态注入元素,以及添加指定标识来进行后续的隔离实现

以及这里设计得比较好的是:基本所有的事情都是通过异步加载的,不阻塞主线程的形式进行操作的呐

pnpm install garfish --save

核心的是使用来说的话分为两种

  • 1. 通过配置化 + 协议化的机制来加载子应用:Garfish.run 来实现对应的加载一个配置呢

// index.js(主应用入口处)
import Garfish from 'garfish';
Garfish.run({
  basename: '/',
  domGetter: '#subApp',
  apps: [
    {
      name: 'react',
      activeWhen: '/react',
      entry: 'http://localhost:3000', // html入口
    },
    {
      name: 'vue',
      activeWhen: '/vue',
      entry: 'http://localhost:8080/index.js', // js入口
    },
  ],
});
  • 2. 通过事件驱动的时候来家子啊吧:Garfish.loadApp 来实现加载子应用吧

// 使用 loadApp 动态挂载应用
import Garfish from 'garfish';
const app = await Garfish.loadApp('vue-app', {
  domGetter: '#container',
  entry: 'http://localhost:3000',
  cache: true,
});

// 若已经渲染触发 show,只有首次渲染触发 mount,后面渲染都可以触发 show 提升性能
app.mounted ? app.show() : await app.mount();

全局变量:window.Garfishwindow.__Garfish__

看了上面的案例之后,我们来看看那个官方给的代码为什么这么写吧

if (!window.__GARFISH__) {
  ReactDOM.render(
    <RootComponent basename="/" />,
    document.querySelector('#root'),
  );
}
  • 当处于的是微前端的环境下时候,那么子应用中就不做任何渲染,走的就是主应用中的那个 Garfish 的逻辑了

  • 当不处于的时候,就是走的是自己的挂载 自己的 html 的 root 容器了吧

这样设计的好处是什么呢??可以实现的是每个应用都可以独立运行,同时也可以联合运行的特性

就是 garfish 的特性之一:独立开发、独立部署、独立测试(在团队开发后期做好对应的开发联调试即可吧)

在框架层面为了保证数据的唯一性,此时就会来一个 symbol 的数据类型吧

值得深度阅读是这些:这些是使用 garfish 作为微前端进行性能优化的核心探究方案吧

https://www.garfishjs.org/guide/concept/bridge.html

核心原理理解

微前端的沙箱机制:通过沙箱机制实现我们的:全局变量冲突、样式冲突

garfish 里面的沙箱的实现核心是依据的是我们的单应用多实例的一个模式呐

VM沙箱,Proxy沙箱,快照沙箱 来实现解决 js 隔离

项目中的使用

  • 在项目中为了解决微前端启动时候的硬编码,此时就简单写了两个node 运行时工具和进行了对应的注册包来解决 DX 吧

  • dev-kit 用来实现的是提供 vite 插件包,运行后实现对应的自动检测子应用的运行 port 和 rpc 的端口,以及主应用生成这样的配置吧

  • app-registry 核心是用来提供运行时的一些注册包的!