某平台的设计模式

219 阅读6分钟

微框架

由于业务的增长,使得业务模块越来越多,多人开发如何协作?如何保持应用的独立隔离,并具有相互通信的功能就成为一个头疼的问题。好在阿里的微前端框架乾坤构建了一个库来帮助你解决在构建微前端系统时可能遇到的这些故障问题,并将其命名为前端,微前端是将微服务的思想运用到前端的体现!

微前端的好处

1.应用自治。只需要遵循统一的接口规范或者框架,以便于系统集成到一起,相互之间是不存在依赖关系的。

2.单一职责。每个前端应用可以只关注于自己所需要完成的功能。

3.技术栈无关。你可以使用 Angular 的同时,又可以使用 React 和 Vue。

微前端的缺点

  • 应用的拆分基础依赖于基础设施的构建,一旦大量应用依赖于同一基础设施,那么维护变成了一个挑战。
  • 拆分的粒度越小,便意味着架构变得复杂、维护成本变高。
  • 技术栈一旦多样化,便意味着技术栈混乱

设计理念

中心化:应用注册表。这个应用注册表拥有每个应用及对应的入口。在前端领域里,入口的直接表现形式可以是路由,又或者对应的应用映射。

标识化应用。 我们需要一个标识符来标识不同的应用,以便于在安装、卸载的时候,能寻找到指定的应用。一个简单的模式,就是通过康威定律来命名应用。

应用生命周期管理。

高内聚,低耦合。

开始

安装

npm i qiankun -S

注册子应用

框架中提供了两种加载子应用的方法。基于路由配置和手动加载微应用

基于路由配置

适用于 route-based 场景。

通过将微应用关联到一些 url 规则的方式,实现当浏览器 url 发生变化时,自动加载相应的微应用的功能。

import { registerMicroApps, setDefaultMountApp, start, runAfterFirstMounted, addGlobalUncaughtErrorHandler } from 'qiankun';

registerMicroApps(apps, lifeCycles?)

registerMicroApps(
  [
    {
      name: 'app1',  // 必选,微应用的名称,微应用之间必须确保唯一。
      entry: '//localhost:8080', // 必选,微应用的入口。
      container: '#container', // 必选,微应用的容器节点的选择器或者 Element 实例
      activeRule: '/qiankun', // 必选,微应用的激活规则。
      props: {
        name: 'kuitos', // 可选,主应用需要传递给微应用的数据。
      },
    },
  ],
  {
    beforeLoad: (app) => console.log('before load', app.name),
    beforeMount: [(app) => console.log('before mount', app.name)],
  },
);

start(opts?)

start();

Options

  • prefetch - boolean | 'all' | string[] | (( apps: RegistrableApp[] ) => { criticalAppNames: string[]; minorAppsName: string[] }) - 可选,是否开启预加载,默认为 true

    配置为 true 则会在第一个微应用 mount 完成后开始预加载其他微应用的静态资源

    配置为 'all' 则主应用 start 后即开始预加载所有微应用静态资源

    配置为 string[] 则会在第一个微应用 mounted 后开始加载数组内的微应用资源

    配置为 function 则可完全自定义应用的资源加载时机 (首屏应用及次屏应用) 除此以外,qiankun 还提供了一个实验性的样式隔离特性,当 experimentalStyleIsolation 被设置为 true 时,qiankun 会改写子应用所添加的样式为所有样式规则增加一个特殊的选择器规则来限定其影响范围,因此改写后的代码会表达类似为如下结构:

// 假设应用名是 react16
.app-main {
  font-size: 14px;
}


div[data-qiankun-react16] .app-main {
  font-size: 14px;
}

setDefaultMountApp(appLink)

设置主应用启动后默认进入的微应用。

setDefaultMountApp('/homeApp');

runAfterFirstMounted(effect)

第一个微应用 mount 后需要调用的方法,比如开启一些监控或者埋点脚本。

runAfterFirstMounted(() => startMonitor());

手动加载微应用

适用于需要手动 加载/卸载 一个微应用的场景。

loadMicroApp(app, configuration?)

import { loadMicroApp } from 'qiankun';
import React from 'react';


class App extends React.Component {
  containerRef = React.createRef();
  microApp = null;


  componentDidMount() {
    this.microApp = loadMicroApp({
      name: 'app1',
      entry: '//localhost:1234',
      container: this.containerRef.current,
      props: { brand: 'qiankun' },
    });
  }


  componentWillUnmount() {
    this.microApp.unmount();
  }


  componentDidUpdate() {
    this.microApp.update({ name: 'kuitos' });
  }


  render() {
    return <div ref={this.containerRef}></div>;
  }
}

如果需要能支持主应用手动 update 微应用,需要微应用 entry 再多导出一个 update 钩子:

export async function mount(props) {
  renderApp(props);
}


// 增加 update 钩子以便主应用手动更新微应用
export async function update(props) {
  renderPatch(props);
}

通信

initGlobalState(state)

子应用通过注册动态加载到主应用中,相当于主应用的一个子组件。父组件和子组件通过props 参数通信,将一些全局对象及方法传入子应用,供其调用修改全局变量。 子应用通过传入的主应用store里是action 方法修改全局的变量

主应用

import { initGlobalState, MicroAppStateActions } from 'qiankun';


// 初始化 state
const actions: MicroAppStateActions = initGlobalState(state);

// 在当前应用监听全局状态,有变更触发 callback,fireImmediately = true 立即触发 callback
actions.onGlobalStateChange((state, prev) => { 
  // state: 变更后的状态; prev 变更前的状态
  console.log(state, prev);
});
actions.setGlobalState(state); // 按一级属性设置全局状态,微应用中只能修改已存在的一级属性
actions.offGlobalStateChange(); // 移除当前应用的状态监听,微应用 umount 时会默认调用

微应用

// 从生命周期 mount 中获取通信方法,使用方式和 master 一致
export function mount(props) {
  props.onGlobalStateChange((state, prev) => {
    // state: 变更后的状态; prev 变更前的状态
    console.log(state, prev);
  });

  props.setGlobalState(state);
}

路由控制

主应用登录后根据不同是权限获取路由表后加载路由,路由表包含所有主要主应用和子应用的全部路由。

权限控制

首先在账号登陆的时候,会返回当前账号全部的权限列表,前端会匹配规则对应加载路由和控制界面ui的显示。当提交api的时候,后端会再次check当前账号的权限。

<a-icon v-if="permission['Mutation:DashBoard:updateDashBoard']" key="setting" type="setting" />
<a-icon v-if="permission['Mutation:DashBoard:updateDashBoard']" key="edit" type="edit" />
<a-icon v-if="permission['Mutation:DashBoard:deleteDashBoard']" key="delete" type="delete" />

业务划分

1.主应用:负责登陆,权限分配,企业添加,计划任务等 2.子应用:负责成员添加,标签添加删除,通讯录等业务

1630996856810.jpg

登录

主应用登陆后获取到token 再注册子应用,注册的时候将token,传入到子应用中,然后统一添加在header头部的Authorization中;如果token失效跳回主应用。

前端模块的划分

主应用中包含

1.登陆模块

里面分为账号登陆模块和扫码登陆

2.权限管理

3.菜单模块

4.账号管理

5.角色管理

6.品牌列表管理

7.全局store管理

8.注册子应用模块

9.父子应用通信管理

子应用中包含

1.标签管理

2.子菜单管理

3.计划任务管理

5.通讯录管理

6.自建应用管理

7.企业信息管理

模块层级的划分

1631074134391.jpg