微前端应用(qiankun+umi+antd)

news2024/11/25 16:23:15

1.微前端介绍以应用选型

1.1什么是微前端?

微前端是一种前端架构模式,它将前端应用程序拆分成多个小型的、独立开发、独立部署的子应用,然后将这些子应用组合成一个大型的、复杂的前端应用。每个子应用都有自己的技术栈、独立的代码库、独立的开发、测试和部署流程,并且可以独立运行、测试和部署。

微前端的目的是解决大型前端应用程序在开发、测试和部署等方面的复杂性和困难。通过将前端应用程序拆分成多个独立的子应用,可以实现团队的分工协作、提高开发效率、降低代码耦合性、提高代码可维护性和可测试性。同时,微前端也可以实现应用程序的增量更新、灰度发布、动态加载和懒加载等功能,从而提高应用程序的性能和用户体验。

1.2技术选择

主应用:umi(Ant Design Pro)

子应用:umi(Ant Design Pro)

父应用和子应用其实都是独立的前端项目,父应用可以在内部引入子应用,子应用也可以在自己内部继续引入孙子应用

2.开始使用

2.1配置父应用

首先需要配置父应用,注册子应用的相关信息,这样父应用才能识别子应用并在内部引入。

注册子应用的方式主要有两种:

  • 插件注册子应用。
  • 运行时注册子应用。

以上两种方式都是在 .umirc.ts 中注册子应用,但是使用的插件不同,从而导致了使用方式的不同。

  1. 插件注册子应用:

使用插件注册子应用时,需要安装 @umijs/plugin-qiankun 插件,并在 .umirc.ts 中进行配置,如下:

import { defineConfig } from 'umi';

export default defineConfig({
  qiankun: {
    slave: {},
    master: {
      apps: [
        {
          name: 'reactApp', // 唯一 id
          entry: 'http://localhost:8091', // html entry
        },
      ],
      // sandbox: false,
    },
  },
});

qiankun 配置中,slave 为子应用的配置,可以在这里配置子应用的名称、入口、路由、公共依赖等信息。该插件会自动构建子应用的入口文件、修改 webpack 配置等。

注意:没有.umirc.ts文件就写在config.ts中,上述中如果不添加slave: {}配置信息,会报错:Unhandled Rejection (Error): register failed, invalid key useQiankunStateForSlave from plugin ../../app.tsx.如果有这个报错请参考这种方法

  1. 运行时注册子应用:

运行时注册子应用时,不需要安装额外的插件,只需要在 .umirc.ts 中进行配置,如下:

import { defineConfig } from 'umi';

export default defineConfig({
  // 省略其他配置
  routes: [
    {
      path: '/',
      component: '@/layouts/index',
      routes: [
        {
          path: '/',
          component: '@/pages/index',
          microApp: 'sub-app', // 运行时注册子应用
        },
      ],
    },
  ],
});

routes 中配置 microApp 字段即可运行时注册子应用。在这里可以配置子应用的名称、入口、路由等信息。

两种方式的目的都是为了在主应用中注册子应用,只是使用的插件和配置方式有所不同。如果使用 @umijs/plugin-qiankun 插件,则可以自动构建子应用的入口文件、修改 webpack 配置等,使用起来更加方便。如果不使用插件,则需要手动配置子应用的入口、路由等信息。

2.2配置子应用

子应用需要导出必要的生命周期钩子,供父应用在适当的时机调用。

假设您的子应用项目基于 Umi 开发引入了 qiankun 插件。如果没有,可以按照此教程进行配置。

修改子应用的 Umi 的配置文件,添加如下内容:

// .umirc.ts
export default {
  qiankun: {
    slave: {},
  },
};

这样,微前端插件会自动在项目中创建好 Qiankun 子应用所需的生命周期钩子和方法.

本地调试时,主应用中点击子应用的路由地址,报错”Unhandled Rejection (TypeError): Failed to fetch”,

要解决这个问题,有两种方法可以尝试:

  1. 配置子应用的跨域规则

在子应用的配置中,通过配置跨域规则来允许主应用访问子应用的资源。具体来说,可以在子应用的配置文件中(例如 umi 项目的 config/config.ts 或 config/config.prod.ts 文件)加入如下配置:

export default {
  // ...
  devServer: {
    // 允许跨域访问的域名,如果有多个可以用逗号隔开
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  },
  // ...
};

这个配置会在子应用启动时,自动为子应用启动一个 devServer 服务,并允许跨域访问。

  2.配置主应用代理

export default {
  // ...
  proxy: {
    '/app1': {
      target: 'http://localhost:8001', // 子应用1的服务地址
      changeOrigin: true,
      pathRewrite: {
        '^/app1': '',
      },
    },
    '/app2': {
      target: 'http://localhost:8002', // 子应用2的服务地址
      changeOrigin: true,
      pathRewrite: {
        '^/app2': '',
      },
    },
    // ...
  },
  // ...
};

这个配置将会把以 /app1 或 /app2 开头的请求,分别代理到子应用1和子应用2所在的服务上。注意,在使用代理时,要确保子应用的服务已经启动并监听了对应的端口号。

2.3引入子应用

在父应用中引入子应用,插件提供了三种不同实现的方式:

  • 路由绑定引入子应用。
  • <MicroApp /> 组件引入子应用。
  • <MicroAppWithMemoHistory /> 组件引入子应用。

2.3.1路由绑定引入子应用

手动配置 config.ts 文件中的 routes 项,通过路由的方式绑定子应用。何时使用:

  • 子应用包含完整的路由切换逻辑时。
  • 父子应用路由相互关联时。

现在,可以配置父应用下的一个子应用"react子应用",路由如下:

{
    name: 'react子应用',
    path: '/subreact',
    microApp: 'reactApp',
    microAppProps: {
      autoSetLoading: true,
    },
    routes: [
      {
        name: 'Welcome',
        icon: 'smile',
        path: '/subreact/Welcome',
        
      },
       {
        name: 'table-list',
        icon: 'table',
        path: '/subreact/list',
         // 如果想要将 /subreact/list 下所有子路由都关联给微应用 app1,可以带上 * 通配符
      },
    ],
  },

配置好后,子应用的路由 base 会在运行时被设置为主应用中配置的 path。 例如,在上面的配置中,我们指定了 react子应用 关联的 path 为 /subreact/Welcome,假如 app1 里有一个路由配置为 /user,当我们想在父应用中访问 /user 对应的页面时,浏览器的 url 需要是 base + /user,即 /subreact/user 路径,否则子应用会因为无法匹配到正确的路由而渲染空白或404页面。

可以手动修改路由配置信息:


if (window.__POWERED_BY_QIANKUN__) {
  routesData.map(item => {
    if (item.path.includes('/')) {
      item.path = '/subreact' + item.path
    }
    if (item.redirect) {
      item.redirect = '/subreact' + item.redirect
    }
    return item
  })
}

2.3.2<MicroApp /> 组件引入子应用

通过 <MicroApp /> 组件加载(或卸载)子应用。何时使用:

  • 子应用包含完整的路由切换逻辑时。
  • 父子应用路由相互关联时。

现在,我们想在父应用的某个页面中引入子应用 app1,可以编写代码如下:

import { MicroApp } from 'umi';
 
export default function Page() {
  return <MicroApp name="subreact" />;
};

使用该方式引入子应用时,父子应用的路由将一一对应。例如,当父应用路由为 /some/page 时,子应用路由同样为 /some/page。切换子应用路由时,父应用将同步切换。

如果父应用的路由包含前缀,可以通过配置 base 属性保证父子应用的路由正确对应。例如,父应用路由为 /main-react/welcome 时,我们希望子应用的路由为 /subreact/welcome,可以修改代码如下:

import { MicroApp } from 'umi';
 
export default function Page() {
  return <MicroApp name="reactApp" base="/main-react" />
};

2.3.3<MicroAppWithMemoHistory /> 组件引入子应用

通过 <MicroAppWithMemoHistory /> 组件加载(或卸载)子应用。何时使用:

  • 仅使用子应用的指定路由时。
  • 父子应用路由相互独立时。

<MicroAppWithMemoHistory /> 组件是 <MicroApp /> 组件的变体,您需要显式提供 url 属性作为子应用的路由。当父应用的路由发生变化时,子应用的路由不会改变

现在,我们想在父应用的某个组件内部引入 subreact 子应用,子应用的路由为 /subreact/welcome,可以编写代码如下:

<MicroAppWithMemoHistory name="reactApp" url="/welcome" />

2.4子应用之间跳转

这个我暂时未尝试成功,会报错MicroAppLink 导出错误问题,成功的小伙伴欢迎补充.下面是具体内容:

如果子应用通过路由绑定的方式引入,在其它子应用的内部,可以使用 <MicroAppLink /> 跳转到对应的路由。以子应用 app1 和 app2 为例:

// 在 app1 中
import { MicroAppLink } from 'umi';
 
export default function Page() {
  return (
    <>
      {/* 跳转链接为 /app2/home */}
      <MicroAppLink name="app2" to="/home">
        <Button>go to app2</Button>
      </MicroAppLink>
    </>
  );
}

在上面的例子中,点击按钮后,父应用的路由变为 /app2/home,渲染子应用 app2 内部路由为 /home 的页面。同理,如果想要从子应用 app2 回到子应用 app1,可以编写代码如下:

// 在 app2 中
import { MicroAppLink } from 'umi';
 
export default function Page() {
  return (
    <>
      {/* 跳转链接为 /app1/project/home */}
      <MicroAppLink name="app1" to="/home"> 
        <Button>go to app1</Button>
      </MicroAppLink>
    </>
  );
}

您也可以从子应用跳转到父应用的指定路由:

// 在子应用中
import { MicroAppLink } from 'umi';
 
export default function Page() {
  return (
    <>
      {/* 跳转链接为 /table */}
      <MicroAppLink isMaster to="/table">
        <Button>go to master app</Button>
      </MicroAppLink>
    </>
  );
}

补充:可以通过路由引用和组件引用同一个子应用

3.子应用生命周期

3.1父应用配置生命周期钩子

在父应用的 src/app.ts 中导出 qiankun 对象进行全局配置,所有的子应用都将实现这些生命周期钩子:

// src/app.ts
export const qiankun = {
  lifeCycles: {
    // 所有子应用在挂载完成时,打印 props 信息
    async afterMount(props) {
      console.log(props);
    },
  },
};

3.2子应用配置生命周期钩子

在子应用的 src/app.ts 中导出 qiankun 对象,实现生命周期钩子。子应用运行时仅支持配置 bootstrapmount 和 unmount 钩子:

/ src/app.ts
export const qiankun = {
  // 应用加载之前
  async bootstrap(props) {
    console.log('app1 bootstrap', props);
  },
  // 应用 render 之前触发
  async mount(props) {
    console.log('app1 mount', props);
  },
  // 应用卸载之后触发
  async unmount(props) {
    console.log('app1 unmount', props);
  },
};

会将引用子组件时传递给子组件的函数和参数都打印出来,这个下面组件传递信息会讲到,且props中包括子应用的信息,如下:

 

4.父子应用通信

父子应用间的通信有两种实现的方法:

  • 基于 useModel() 的通信。这是 Umi 推荐的解决方案。
  • 基于配置的通信。

4.1基于 useModel() 的通信

该通信方式基于 数据流 插件,此插件已经内置于 @umi/max 解决方案当中。

该通信方式需要子应用基于 Umi 开发引入了该数据流插件

关于此插件的详细介绍可见数据流指南。

4.1.1主应用透传数据

如果通过路由的模式引入子应用,则需要在父应用的 src/app.ts 里导出一个名为 useQiankunStateForSlave() 的函数,该函数的返回值将传递给子应用:

// src/app.ts
export function useQiankunStateForSlave() {
  const [globalState, setGlobalState] = useState<any>({
    slogan: 'Hello MicroFrontend',
  });
 
  return {
    globalState,
    setGlobalState,
  };
}

如果通过组件的模式引入子应用,直接将数据以组件参数的形式传递给子应用即可:


import React, { useState } from 'react';
import { MicroApp } from 'umi';
 
export default function Page() {
  const [globalState, setGlobalState] = useState<any>({
    slogan: 'Hello MicroFrontend',
  });
 
  return (
    <MicroApp
      name="app1"
      globalState={globalState}
      setGlobalState={setGlobalState}
    />
  );
};

4.1.2子应用消费数据

子应用会自动生成一个全局的 Model,其命名空间为 @@qiankunStateFromMaster。通过 useModel() 方法,允许子应用在任意组件中获取并消费父应用透传的数据,如下所示:

import { useModel } from 'umi';
 
export default function Page() {
  const masterProps = useModel('@@qiankunStateFromMaster');
  return <div>{JSON.stringify(masterProps)}</div>;
};

或者可以通过高阶方法 connectMaster() 来获取并消费父应用透传的数据,如下所示:

import { connectMaster } from 'umi';
 
function MyPage(props) {
  return <div>{JSON.stringify(props)}</div>;
}
 
export default connectMaster(MyPage);

子应用也可以在生命周期钩子中能够获取并消费得到的 props 属性,根据需求实现对应的生命周期钩子即可。

特别的,当父应用使用 <MicroApp /> 或 <MicroAppWithMemoHistory /> 组件的方式引入子应用时,会额外向子应用传递一个 setLoading() 方法,允许子应用在合适的时机执行,标记子应用加载为完成状态:

const masterProps = useModel('@@qiankunStateFromMaster');
masterProps.setLoading(false);
 
// 或者
function MyPage(props) {
  props.setLoading(false);
}
connectMaster(MyPage);

注:setLoading() 方法可以和自定义加载配套使用,在主应用中通过组件引入子应用时,自定义loader,如下:

//CustomLoader.tsx
import { Alert, Spin } from 'antd';
import * as React from 'react';

interface ICustomLoaderProps {
    loading: boolean
}


const CustomLoader: React.FC<ICustomLoaderProps> = (props) => {
    const { loading } = props
    return <Spin spinning={loading}>
    </Spin>;
};

export default CustomLoader;
import CustomLoader from '@/components/CustomLoader';
<MicroAppWithMemoHistory name="reactApp"
          url="/welcome"
          loader={(loading) => <CustomLoader loading={loading} />} />

下面例举一下组件引用方式时,子应用通过父应用传递过来的方法修改父应用中的值:

const masterProps = useModel('@@qiankunStateFromMaster');

const onChangeGlobalState = () => {
  const updatedGlobalState = {
    ...masterProps?.globalState,
    slogan: '我通过子应用成功修改父应用中的值',
  };

  masterProps?.setGlobalState(updatedGlobalState);
};

注意:

子应用中使用 useModel 获取到的 masterProps 对象包含了从主应用中传递过来的 globalStatesetGlobalState 方法。然而,直接使用 setGlobalState 修改 globalState 时可能无法实现更新。

这是因为 masterProps 中包含的 globalState 是主应用中的状态,子应用不能直接修改主应用中的状态。子应用只能通过 setGlobalState 方法向主应用传递更新后的状态,并让主应用自己进行更新。因此,在子应用中使用 setGlobalState 方法时,需要将整个更新后的状态对象作为参数传递给 setGlobalState 方法。而不能直接修改 globalState 对象中的某个属性值。

正确的做法是,在子应用中使用 setGlobalState 方法时,先从 masterProps 中获取当前的 globalState 对象,然后将需要更新的属性值进行修改,最后将整个更新后的 globalState 对象传递给 setGlobalState 方法。

或者可以使用下面这种方法:

import { useModel } from 'umi';
import { Button } from 'antd';

function SubApp() {
  const { globalState, setGlobalState } = useModel('@@qiankunStateFromMaster');
  const handleClick = () => {
    setGlobalState({
      ...globalState,
      slogan: 'New Slogan from Sub App',
    });
  };
  return (
    <div>
      <h1>{globalState.slogan}</h1>
      <Button onClick={handleClick}>更改父应用globalState</Button>
    </div>
  );
}

export default SubApp;

参考文档:

微前端 | UmiJS

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/513853.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux快捷命令

目录 一、快捷排序——sort 常用选项&#xff1a; 示例 二、快捷去重——uniq 常用选项&#xff1a; 示例&#xff1a; ​编辑 ​编辑 ​编辑 三、快捷替换——tr 用于windows的编写的脚本格式转换为Linux格 方法一&#xff1a; 方法二&#xff1a; 四、快速裁…

JAVA double精度丢失问题

double类型精度丢失问题&#xff1a; 0.1*0.1使用计算器计算是0.01&#xff0c;代码里却是0.010000000000000002 public class HelloWorld {public static void main(String []args) {double number1 0.1;double number2 0.1;double result number1 * number2 ;System.o…

CSP-S 2022 提高级 第一轮 阅读程序(1) 第16-21题

【题目】 CSP-S 2022 提高级 第一轮 阅读程序&#xff08;1&#xff09; 第16-21题 01 #include <iostream> 02 #include <string> 03 #include <vector> 04 05 using namespace std; 06 07 int f(const string &s, const string &t) 08 { …

关于cartographer建立正确关系树的理解

正确的TF关系map----odom----base_link----laser base_link是固定在机器人本体上的坐标系&#xff0c;通常选择飞控 其中map–odom 的链接是由cartographer中lua文件配置完成的 map_frame "map", tracking_frame "base_link", published_frame "b…

Ubuntu 20.04 安装 mysql8 并配置远程访问

文章目录 一、使用 apt-get 安装 mysql 服务二、初始化 mysql 数据库管理员用户密码三、配置远程访问 一、使用 apt-get 安装 mysql 服务 # 更新软件源 apt-get install update# 安装mysql服务 apt-get install mysql-server# 使用mysqladmin工具查看mysql版本 mysqladmin --v…

一文解析Linux进程的睡眠和唤醒

Linux进程的睡眠和唤醒 在Linux中&#xff0c;仅等待CPU时间的进程称为就绪进程&#xff0c;它们被放置在一个运行队列中&#xff0c;一个就绪进程的状 态标志位为 TASK_RUNNING。一旦一个运行中的进程时间片用完&#xff0c; Linux 内核的调度器会剥夺这个进程对CPU的控制权&…

燃气巡检二维码

对燃气公司的输气管道和阀井等设施的巡检工作的管理目标是能降低成本、提高工作效率以及管理水平。但用纸质记录的方式进行燃气设备巡检有以下缺点&#xff1a; 1、难保证巡检真实性 无法客观、方便地掌握巡检人员巡检的到位情况&#xff0c;因而无法有效地保证巡检工作人员按计…

软件兼容性测试如何进行?怎么选择靠谱的软件检测公司?

软件兼容性测试是一项非常重要的工作&#xff0c;能够确保在不同的操作系统、设备、浏览器以及其他软件环境下&#xff0c;软件应用都能够正常运行。 一、软件兼容性测试如何进行? 确定测试的环境&#xff0c;包括操作系统、设备、浏览器等&#xff0c;并建立测试用例和测试…

Maven必要知识

参考笔记&#xff1a; https://www.wolai.com/arAiYJYCr6Kkfi2kZ8HxE8 1. Maven 概述 1.1 什么是 Maven Maven 是 Apache 软件基金会组织维护的一款专门为 Java 项目提供构建和依赖管理支持的工具。 Maven 作为依赖管理工具 jar 包的管理jar 包的来源jar 包之间的依赖关系…

使用R语言绘制折线图

R语言绘制折线图 一、绘制折线图1.载入bruceR&#xff08;ggplot2&#xff09;2.设置当前工作目录3.载入数据集4.查看数据结构5.绘制基础图形6.图形优化 二、绘制多重折线图1.载入数据2.绘制图形 一、绘制折线图 1.载入bruceR&#xff08;ggplot2&#xff09; &#xff08;要…

【HAL库】STM32CubeMX开发----非阻塞延时实验----SysTick(滴答定时器)中断

STM32CubeMX 下载和安装 详细教程 【HAL库】STM32CubeMX开发----STM32F103/F207/F407----目录 前言 HAL库 有自带的 ms级 延时函数&#xff1a; HAL_Delay(); 缺点&#xff1a; 这是阻塞延时方式&#xff0c;就是延时期间&#xff0c;什么都不能干&#xff0c;这样很浪费资源。…

便携式车用CAN分析仪

产品简介 USBCAN-C系列便携式车用CAN分析仪&#xff0c;通过USB接口快速扩展一路CAN通道&#xff0c;使接入CAN网络非常容易&#xff0c;它具有一体式和小巧紧凑的外形&#xff0c;特别适合于随身携带。CAN接口采用金升阳电源模块和信号隔离芯片实现2500V DC电气隔离&#xff0…

能源设备智能维修AR远程作业指导平台降低运营成本

AR远程专家指导系统是一种基于AR增强现实技术搭建的远程协作解决方案&#xff0c;它可以让专家全视角掌握操作现场&#xff0c;并将专精知识和技能传递给远程现场的工作人员&#xff0c;以帮助他们解决各种技术难题和困难&#xff0c;赋能各行各业行业。 现场人员通过手机、平板…

OJ练习第104题——格雷编码

格雷编码 力扣链接&#xff1a;89. 格雷编码 题目描述 n 位格雷码序列 是一个由 2n 个整数组成的序列&#xff0c;其中&#xff1a; 每个整数都在范围 [0, 2n - 1] 内&#xff08;含 0 和 2n - 1&#xff09; 第一个整数是 0 一个整数在序列中出现 不超过一次 每对 相邻 整数…

数据流图(DFD)这么理解吗?

如何画好数据流程图&#xff08;DFD&#xff09;&#xff1f; 步骤&#xff1a; 确定系统的 input 和 output。由 表层 到 深层 画系统的顶层数据流图。自顶向下 逐层 分解&#xff0c;画出分层数据流图。 一、理解数据流图 基本概念 ~~ 数据流图 数据流&#xff08;箭头&…

linux pinctrl 和 gpio 子系统 LED驱动

pinctrl 和 gpio 子系统 借助 pinctrl 和 gpio 子系统来简化 GPIO 驱动开发 pinctrl 子系统 pinctrl 子系统&#xff08;drivers/pinctrl&#xff09;的主要工作内容&#xff1a; ①、获取设备树中 pin 信息。 ②、根据获取到的 pin 信息来设置 pin 的复用功能 ③、根据获…

校招失败后,4面字节跳动软件测试工程师,竭尽全力....

下面是我面试字节跳动软件测试工程师的面试经验总结&#xff0c;希望能帮助到你们。 面试一 简单做一下自我介绍简要介绍一下项目/你负责的模块/选一个模块说一下你设计的用例get请求和post请求的区别如何判断前后端bug/3xx是什么意思说一下XXX项目中你做的接口测试/做了多少次…

免费润色文章的软件-自动修改文章润色的软件

免费润色文章的软件 免费润色文章的软件可以帮助用户快速地改善文本质量&#xff0c;进一步提高语言表达能力和流畅性&#xff0c;以下是其主要优势&#xff1a; 高效性&#xff1a;免费润色文章的软件能够快速进行润色处理&#xff0c;为用户节省时间和精力。相比手动润色的方…

瑞芯微RK3588核心板远程会诊等医学解决方案

RK3588处理器在医学领域的应用中&#xff0c;可以为远程会诊提供高性能和可靠的解决方案。以下是基于RK3588的远程会诊医学方面的解决方案&#xff1a; 远程高清图像传输&#xff1a; 利用RK3588处理器的高性能图像处理能力和高速网络接口&#xff0c;实现高清医学图像的实时传…

Dubbo 基于xml文件分析主流程源码 (4)

目录 前提 JDK实现SPI Dubbo实现SPI Dubbo源码 1. 找到Dubbo的命名空间处理类&#xff0c;也就是Dubbo的入口类 2. 将dubbo标签交给spring进行管理&#xff0c;就是从 BeanDefinition----> Bean的过程。 3. 服务暴露 4. 服务引入 总结 仿写Dubbo 前提 1. Dubbo源码…