0


Umi Max 详解:打造高性能、可扩展的前端应用

UMI是蚂蚁金服的底层前端框架,也是一个基于React的企业级前端应用框架,它提供了开箱即用的项目脚手架和插件化的配置,如路由构建、部署测试、文档工具、请求库等,帮助开发者快速搭建和管理复杂的前端项目,其设计目标是提高前端项目的开发效率和可维护性,尤其适用于大型复杂项目的开发与管理。

初识Umi Max

了解框架:为了方便开发者更加方便的使用umi提供的插件,umi团队在这些插件开源的基础上,直接将其集成到一起,打造了@umijs/max,让开发者直接可以通过脚手架马上获得和蚂蚁集团开发umi应用一样的开发体检,只需要在使用create-umi选择Ant Design Pro模板,就能使用@umijs/max来创建项目了,可以参考官方文档:地址 ,详细了解umi max的相关开发:

因为umi max是使用Ant Design Pro模板进行开发的,所以我们也需要了解 Ant Design Pro 对应相关配置和API的使用,通过查阅官方文档进行详细了解:

当然我们在使用antd的时候,有一些组件的是十分细碎的,这里我们可以参考ProComponents ,其可以让让中后台开发更简单,如下所示:

像登录表单的内容,ProComponents组件库已经帮助我们封装好了,如下图所示:

安装项目:接下来我们开始安装umi max项目,安装的方式和umi项目命令一样,只需要在进行模板选择的时候,选择Ant Design Pro模板即可:

安装完之后直接拖到编辑器中执行 pnpm run dev 运行项目即可,最终呈现的效果如下所示:

当然在使用 Ant Design Pro 的时候,除了使用 umi 进行安装项目,我们也可以使用官方推荐给我们的pro-cli脚手架进行安装项目,如下图所示:

Umi Max数据流

官方文档给我们介绍到,umi max给我们内置了数据流管理插件,它是一种基于hooks范式的轻量级数据管理方案,可以在umi项目中管理全局的共享数据,从官方文档可以看到umi max规定的数据流和相关命名规范方面的内容,如下所示:

这里我们在src下的models目录下新建一个ts文件,用于全局状态管理的设置,这里我们可以使用react提供的相关API函数进行书写,书写的方式有点类似pinia状态管理的写法,条理十分清晰:

  1. import { useState, useCallback,useEffect } from "react";
  2. export default function countModel() {
  3. const [count, setCount] = useState(0);
  4. // setCount修改状态是异步的,所以需要使用useCallback包裹一下
  5. const add = useCallback(() => setCount(count + 1) , [count]);
  6. const minus = useCallback(() => setCount(count - 1), [count]);
  7. // 设置两秒之后修改count的值
  8. useEffect(() => {
  9. setTimeout(() => {
  10. setCount(100);
  11. }, 2000)
  12. }, []);
  13. // 返回count和两个方法
  14. return {
  15. count,
  16. add,
  17. minus
  18. }
  19. }

定义好仓库之后,接下来我们就需要使用仓库中的数据了,使用方式也是非常简单,直接借助umi框架提供的api函数useModel即可,具体代码如下所示:

最终呈现的效果如下所示:

性能优化:官方文档提供了一个性能优化的方案,useModel() 方法可以接受可选的第二个参数,当组件只需要使用Model中的部分参数,而对其它参数的变化不感兴趣时,可以传入一个函数进行过滤。

  1. 组件并不关心计数器Model中的counter值,只需要使用Model提供的increment()和decrement()方法,于是传入了一个函数作为useModel() 方法的第二个参数,该函数的返回值将作为useModel()方法的返回值,这样过滤掉了counter这一频繁变化的值,避免了组件重复渲染带来的性能损失,以实现计数器的操作按钮为例:
  1. // src/components/CounterActions/index.tsx
  2. import { useModel } from 'umi';
  3. export default function Page() {
  4. const { add, minus } = useModel('counterModel', (model) => ({
  5. add: model.increment,
  6. minus: model.decrement,
  7. }));
  8. return (
  9. <div>
  10. <button onClick={add}>add by 1</button>
  11. <button onClick={minus}>minus by 1</button>
  12. </div>
  13. );
  14. };

全局初始状态:@umi/max内置了全局初始状态管理插件,可以快速构建并在组件内获取umi项目全局的初始状态,全局初始状态在整个umi项目的最开始创建,编写src/app.ts的导出方法getInitialState(),其返回值将成为全局初始状态。例如:

  1. // src/app.ts
  2. import { fetchInitialData } from '@/services/initial';
  3. export async function getInitialState() {
  4. const initialData = await fetchInitialData();
  5. return initialData;
  6. }

现在,各种插件和定义的组件都可以通过useModel('@@initialState')直接获取到这份全局的初始状态,如下所示:

  1. import { useModel } from 'umi';
  2. export default function Page() {
  3. const { initialState, loading, error, refresh, setInitialState } =
  4. useModel('@@initialState');
  5. return <>{initialState}</>;
  6. };

接口请求

request:umi提供了内置的请求接口的API函数,它基于axios和ahooks的useRequest提供了一套统一的网络请求和错误处理方案,如下:

  1. import { request } from '@umijs/max';

然后我们在接口文件里面直接使用类似axios的写法即可,接口引入接口函数调用,示例如下:

  1. export async function testApi() {
  2. return request("https://api.uomg.com/api/rand.qinghua")
  3. }

最终呈现的效果如下所示:

useRequest:官方文档也是给我们提供了useRequest这个API,帮我我们更好的去消费数据:

  1. import { useRequest } from 'umi';
  2. export default function Page() {
  3. const { data, error, loading } = useRequest(() => {
  4. return services.getUserList('/api/test');
  5. });
  6. if (loading) {
  7. return <div>loading...</div>;
  8. }
  9. if (error) {
  10. return <div>{error.message}</div>;
  11. }
  12. return <div>{data.name}</div>;
  13. };

上面的是什么意思呢?就是说当我们创建好接口函数之后,想要调用接口就可以使用useRequest对数据进行相应的处理,这里给出基础案例:

上面代码中可以看到我们是直接使用了data属性就能获取对应的数据,而不需要再通过链式操作一层一层的去寻找我们的数据,这是为啥呢?原来umi已经帮我们封装好了配置项:

我们在构建时的配置项中已经配置好了相应的属性下的值,从而不需要再data.content去拿数据:

请求响应报错拦截:在 src/app.ts 中你可以通过配置 request 项,来为你的项目进行统一的个性化的请求设定。

  1. import type { RequestConfig } from '@umijs/max';
  2. export const request: RequestConfig = {
  3. timeout: 1000,
  4. errorConfig: {
  5. errorHandler(){
  6. },
  7. errorThrower(){
  8. }
  9. },
  10. requestInterceptors: [],
  11. responseInterceptors: []
  12. };

这里官方给出一个完整的运行时配置示例,以帮助能够更好的去为自己的项目设定个性化的请求方案:

  1. import { RequestConfig } from './request';
  2. // 错误处理方案: 错误类型
  3. enum ErrorShowType {
  4. SILENT = 0,
  5. WARN_MESSAGE = 1,
  6. ERROR_MESSAGE = 2,
  7. NOTIFICATION = 3,
  8. REDIRECT = 9,
  9. }
  10. // 与后端约定的响应数据格式
  11. interface ResponseStructure {
  12. success: boolean;
  13. data: any;
  14. errorCode?: number;
  15. errorMessage?: string;
  16. showType?: ErrorShowType;
  17. }
  18. // 运行时配置
  19. export const request: RequestConfig = {
  20. // 统一的请求设定
  21. timeout: 1000,
  22. headers: {'X-Requested-With': 'XMLHttpRequest'},
  23. // 错误处理: umi@3 的错误处理方案。
  24. errorConfig: {
  25. // 错误抛出
  26. errorThrower: (res: ResponseStructure) => {
  27. const { success, data, errorCode, errorMessage, showType } = res;
  28. if (!success) {
  29. const error: any = new Error(errorMessage);
  30. error.name = 'BizError';
  31. error.info = { errorCode, errorMessage, showType, data };
  32. throw error; // 抛出自制的错误
  33. }
  34. },
  35. // 错误接收及处理
  36. errorHandler: (error: any, opts: any) => {
  37. if (opts?.skipErrorHandler) throw error;
  38. // 我们的 errorThrower 抛出的错误。
  39. if (error.name === 'BizError') {
  40. const errorInfo: ResponseStructure | undefined = error.info;
  41. if (errorInfo) {
  42. const { errorMessage, errorCode } = errorInfo;
  43. switch (errorInfo.showType) {
  44. case ErrorShowType.SILENT:
  45. // do nothing
  46. break;
  47. case ErrorShowType.WARN_MESSAGE:
  48. message.warn(errorMessage);
  49. break;
  50. case ErrorShowType.ERROR_MESSAGE:
  51. message.error(errorMessage);
  52. break;
  53. case ErrorShowType.NOTIFICATION:
  54. notification.open({
  55. description: errorMessage,
  56. message: errorCode,
  57. });
  58. break;
  59. case ErrorShowType.REDIRECT:
  60. // TODO: redirect
  61. break;
  62. default:
  63. message.error(errorMessage);
  64. }
  65. }
  66. } else if (error.response) {
  67. // Axios 的错误
  68. // 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
  69. message.error(`Response status:${error.response.status}`);
  70. } else if (error.request) {
  71. // 请求已经成功发起,但没有收到响应
  72. // \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,
  73. // 而在node.js中是 http.ClientRequest 的实例
  74. message.error('None response! Please retry.');
  75. } else {
  76. // 发送请求时出了点问题
  77. message.error('Request error, please retry.');
  78. }
  79. },
  80. },
  81. // 请求拦截器
  82. requestInterceptors: [
  83. (config) => {
  84. // 拦截请求配置,进行个性化处理。
  85. const url = config.url.concat('?token = 123');
  86. return { ...config, url};
  87. }
  88. ],
  89. // 响应拦截器
  90. responseInterceptors: [
  91. (response) => {
  92. // 拦截响应数据,进行个性化处理
  93. const { data } = response;
  94. if(!data.success){
  95. message.error('请求失败!');
  96. }
  97. return response;
  98. }
  99. ]
  100. };

当然unimax还有一些其他有趣的功能,这里就不再一一赘述了,感兴趣的朋友可自行查阅文档,后面项目中遇到的话,博主在进行讲解。

标签: 前端 react umi

本文转载自: https://blog.csdn.net/qq_53123067/article/details/140533380
版权归原作者 亦世凡华、 所有, 如有侵权,请联系我们删除。

“Umi Max 详解:打造高性能、可扩展的前端应用”的评论:

还没有评论