前端初学者的Ant Design Pro V6总结(下)

06-01 1020阅读

前端初学者的Ant Design Pro V6总结(上)

前端初学者的Ant Design Pro V6总结(下)

文章目录

    • @umi 请求相关
      • 一个能用的请求配置
      • Service层 TS 类型规范
      • Service层 函数定义
      • @umi 请求代理 Proxy
      • @umi/max 简易数据流
        • useModel 没有类型提示?
        • useModel 书写规范
        • ProForm 复杂表单
          • 当外部数据发生变化,ProForm不更新?
          • ProForm onFinish中请求错误,提交按钮一直Loading
          • EditorTable 可编辑表格
            • 提交按钮一直Loading?
            • columns 自定义表单、自定义渲染
            • form / formRef 的 setFieldValue / getFieldsValue 无效?
            • Upload / ProUploader 文件上传
              • ImgCrop 实现图片裁切
              • ImgCrop 组件注意事项
              • StepsForm 分布表单
                • 如何在 StepsForm 中 更新子表单?
                • 如何手动控制 步骤 前进、后退?
                • 微前端 Qiankun
                  • 子应用配置(@umi)
                  • 父应用配置(@umi/max)

                    @umi 请求相关

                    一个能用的请求配置

                    Antd Pro的默认的请求配置太复杂了,我写了个简单的,能用,有需要可以做进一步拓展。

                    import { message } from 'antd';
                    import { history } from '@umijs/max';
                    import type { RequestOptions } from '@@/plugin-request/request';
                    import { RequestConfig } from '@@/plugin-request/request';
                    import { LOGIN_URL } from '@/common/constant';
                    export const httpCodeDispose = async (code: string | number) => {
                      if (code.toString().startsWith('4')) {
                        message.error({ content: `请求错误` });
                        if (code === 401) {
                          message.error({ content: `登录已过期,请重新登录` });
                          history.replace({ pathname: LOGIN_URL });
                        }
                        if (code === 403) {
                          message.error({ content: `登录已过期,请重新登录` });
                          localStorage.removeItem('UserInfo');
                          history.replace({ pathname: LOGIN_URL });
                        }
                      }
                      // 500状态码
                      if (code.toString().startsWith('5')) {
                        message.error({ content: `服务器错误,请稍后再试` });
                      }
                    };
                    // 运行时配置
                    export const errorConfig: RequestConfig = {
                      // 统一的请求设定
                      timeout: 20000,
                      headers: { 'X-Requested-With': 'XMLHttpRequest' },
                      // 错误处理: umi@3 的错误处理方案。
                      errorConfig: {
                        /**
                         * 错误接收及处理,主要返回状态码非200,Axios错误的情况
                         * @param error 错误类型
                         * @param opts 请求参数,请求方法
                         */
                        errorHandler: async (error: any, opts: any) => {
                          if (opts?.skipErrorHandler) throw error;
                          // 我们的 errorThrower 抛出的错误。
                          if (error.response) {
                            // Axios 的错误
                            // 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
                            if ((error.message as string).includes('timeout')) {
                              message.error('请求错误,请检查网络');
                            }
                            await httpCodeDispose(error.response.status);
                          } else if (error.request) {
                            // 请求已经成功发起,但没有收到响应
                            // \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,
                            // 而在node.js中是 http.ClientRequest 的实例
                            // message.error('无服务器相应,请重试');
                          } else {
                            // 发送请求时出了点问题
                            message.error('请求错误,请重试');
                          }
                        },
                      },
                      // 请求拦截器
                      requestInterceptors: [
                        (config: RequestOptions) => {
                          // 拦截请求配置,进行个性化处理。
                          const userInfo = JSON.parse(localStorage.getItem('UserInfo') ?? '{}');
                          const token = userInfo.token ?? '';
                          const headers = {
                            ...config.headers,
                            'Content-Type': 'application/json',
                            Whiteverse: token,
                            // Authorization: {
                            //   key: 'Whiteverse',
                            //   value: `Bearer ${token}`
                            // },
                          };
                          return { ...config, headers };
                        },
                      ],
                      /**
                       * 响应拦截器,主要处理服务器返回200,但是实际请求异常的问题
                       */
                      responseInterceptors: [
                        (response: any) => response,
                        (error: any) => {
                          const code = error.data.code;
                          if (!code.toString().startsWith('2')) {
                            httpCodeDispose(code);
                            return Promise.reject(error);
                          }
                          return error;
                        },
                      ],
                    };
                    

                    Service层 TS 类型规范

                    目前团队采用 [name].d.ts 的方式定义公用类型

                    - src > - types > 
                    	service.d.ts
                    	env.d.ts
                    	module.d.ts
                    

                    服务层命名 nameplace 要求全部大写

                    type SortOrder = 'descend' | 'ascend' | null;
                    /**
                     * 通用API
                     */
                    declare namespace API {
                      type Response = {
                        message: string;
                        code: number;
                        data: T;
                      };
                      type QuerySort = Record;
                    }
                    declare namespace COMMON {
                      interface Select {
                        value: string;
                        label: string;
                      }
                    }
                    /**
                     * 分页相关
                     */
                    declare namespace PAGINATE {
                      type Data = { total: number; data: T };
                      type Query = { current?: number; pageSize?: number };
                    }
                    /**
                     * 用户服务相关
                     */
                    declare namespace USER {
                      /**
                       * 用户
                       */
                      interface User {
                        id: string;
                        /**
                         * 头像
                         */
                        avatar: string;
                        /**
                         * 昵称
                         */
                        nickname: string;
                      }
                      /**
                       * 用户基本信息
                       */
                      type UserInfo = Omit;
                      type UsersQuery = PAGINATE.Query & {
                        sort?: API.QuerySort;
                        nickname?: string;
                        mobile?: string;
                        roleId?: string;
                      };
                      /**
                       * 创建用户
                       */
                      type Create = Omit;
                      /**
                       * 登录信息
                       */
                      interface Login {
                        Mobile: string;
                        VerificationCode: string;
                      }
                      /**
                       * 管理员登录参数
                       */
                      interface ALoginParam {
                        Mobile: string;
                        VerificationCode: string;
                      }
                      /**
                       * 验证码
                       */
                      interface Captcha {
                        base64: string;
                        id: string;
                      }
                    }
                    

                    Service层 函数定义

                    1. 为了与普通的函数做区别,方法名全部大写
                    2. 使用 PREFIX_URL 请求前缀,方便后期维护

                    src -> services -> activity -> index.ts

                    export async function GetActivityList(
                      body: ACTIVITY.ActivitiesQuery,
                      options?: { [key: string]: any },
                    ) {
                      return request(`${PREFIX_URL}/activity/list`, {
                        method: 'POST',
                        data: body,
                        ...(options || {}),
                      });
                    }
                    

                    @umi 请求代理 Proxy

                    在开发阶段,如果后端服务的端口经常发生变化,可以使用umi 请求代理 替换原有的请求前缀,转发请求。

                    /**
                     * @name 代理的配置
                     * @see 在生产环境 代理是无法生效的,所以这里没有生产环境的配置
                     * -------------------------------
                     * The agent cannot take effect in the production environment
                     * so there is no configuration of the production environment
                     * For details, please see
                     * https://pro.ant.design/docs/deploy
                     *
                     * @doc https://umijs.org/docs/guides/proxy
                     */
                    export default {
                      // 如果需要自定义本地开发服务器  请取消注释按需调整
                      dev: {
                        '/api-mock/': {
                          // 要代理的地址
                          target: 'http://127.0.0.1:4523/m1/3280694-0-default',
                          // 配置了这个可以从 http 代理到 https
                          // 依赖 origin 的功能可能需要这个,比如 cookie
                          changeOrigin: true,
                          pathRewrite: { '^/api-mock': '' },
                        },
                        '/api-sys/': {
                          // 要代理的地址
                          target: 'http://192.168.50.131:8021',
                          // 配置了这个可以从 http 代理到 https
                          // 依赖 origin 的功能可能需要这个,比如 cookie
                          changeOrigin: true,
                          pathRewrite: { '^/api-sys': '' },
                        },
                        '/api-user/': {
                          // 要代理的地址
                          target: 'http://192.168.50.131:8020',
                          // 配置了这个可以从 http 代理到 https
                          // 依赖 origin 的功能可能需要这个,比如 cookie
                          changeOrigin: true,
                          pathRewrite: { '^/api-user': '' },
                        },
                      },
                      /**
                       * @name 详细的代理配置
                       * @doc https://github.com/chimurai/http-proxy-middleware
                       */
                      test: {
                        // localhost:8000/api/** -> https://preview.pro.ant.design/api/**
                        '/api/': {
                          target: 'https://proapi.azurewebsites.net',
                          changeOrigin: true,
                          pathRewrite: { '^': '' },
                        },
                      },
                      pre: {
                        '/api/': {
                          target: 'your pre url',
                          changeOrigin: true,
                          pathRewrite: { '^': '' },
                        },
                      },
                    };
                    

                    @umi/max 简易数据流

                    useModel 没有类型提示?

                    还原 tsconfig.json 为默认配置

                    {
                      "extends": "./src/.umi/tsconfig.json"
                    }
                    

                    useModel 书写规范

                    定义Model仓库时,推荐使用匿名默认导出语法

                    export default () => {}
                    

                    如果为页面绑定Model,注意页面的层级不要过深,页面组件的名称尽量短

                    • 文件名定义
                      - pages
                      	- Activity
                      		- components
                      			- ActivityList.tsx
                      		- models
                      			- ActivityModels.ts
                      
                      • 使用Model
                        const { getActivityData } = useModel('Activity.ActivityModels', (models) => ({
                        	getActivityData: models.getActivityData,
                        }));
                        

                        带有分页查询的 Model

                        带有loading,query,分页

                        可使用Ahooks 的 useRequest 或 自定封装 useRequest

                        注意Ahooks的 usePagination函数 对Service层的参数有要求

                        • service 的第一个参数为 { current: number, pageSize: number }
                        • service 返回的数据结构为 { total: number, list: Item[] }
                        • 具体看Ahooks文档,不推荐使用或二封分页Hook.
                          import { useEffect, useState } from 'react';
                          import { useSetState } from 'ahooks';
                          import to from 'await-to-js';
                          import { GetActivityList } from '@/services/activity';
                          export default () => {
                            const initialParam = { current: 1, pageSize: 20 };
                            const [query, queryChange] = useSetState(initialParam);
                            const [loading, setLoading] = useState(false);
                            const [error, setError] = useState();
                            const [activityData, setActivityData] = useState();
                            const [total, setTotal] = useState(0);
                            const getActivityData = async (_param: ACTIVITY.ActivitiesQuery) => {
                              // 请求前
                              if (loading) await Promise.reject();
                              // 请求中
                              setLoading(true);
                              const [err, res] = await to(GetActivityList(_param));
                              setLoading(false);
                              // 请求结束
                              if (!err && res.code === 200) {
                                setActivityData(res.data.data);
                                setTotal(res.data.total);
                                return res.data;
                              } else {
                                setError(err);
                                return await Promise.reject();
                              }
                            };
                            useEffect(() => {
                              if (!activityData) getActivityData(query);
                            }, []);
                            return {
                              // 状态
                              loading,
                              setLoading,
                              error,
                              setError,
                              query,
                              queryChange,
                              total,
                              setTotal,
                              activityData,
                              setActivityData,
                              // 方法
                              getActivityData,
                            };
                          };
                          

                          ProForm 复杂表单

                          当外部数据发生变化,ProForm不更新?

                          解决方案一:

                          前端初学者的Ant Design Pro V6总结(下)
                          (图片来源网络,侵删)
                          // 监测外部值的变化,更新表单内的数据
                          useEffect(() => formRef.current && formRef.current.setFieldsValue(selectedNode), [selectedNode]);
                          

                          解决方案二:

                           {
                          		formRef.current?.resetFields();
                          		const res = await GetRole({id: params.id});
                          		return res.data
                          	}}
                          >
                          // ...	
                          
                          

                          ProForm onFinish中请求错误,提交按钮一直Loading

                          onFinish 方法需要返回一个Promise.resolve(boolean),reject时,会一直loading

                          前端初学者的Ant Design Pro V6总结(下)
                          (图片来源网络,侵删)

                          一个综合案例

                          const handleAddActivity = async (fields: ACTIVITY.Create) => {
                          	const hide = message.loading('正在创建活动');
                          	try {
                          		const response = await CreateActivity({ ...fields });
                          		hide();
                          		message.success('活动创建成功!');
                          		return response;
                          	} catch (error) {
                          		hide();
                          		message.error('添加失败,请重试!');
                          		return Promise.reject(false);
                          	}
                          };
                          "创建活动"}
                            stepProps={{
                              description: "请输入活动信息",
                            }}
                            onFinish={async (formData: ACTIVITY.Create & { ActivityTime?: string[] }) = {
                              try {
                                const requestBody = { ...formData };
                                requestBody.StartTime = formData.ActivityTime![0];
                                requestBody.EndTime = formData.ActivityTime![1]!;
                                delete requestBody["ActivityTime"];
                                const response = await handleAddActivity(requestBody);
                                const ActivityId = response.data;
                                uploadFormsRef.current?.setFieldValue("ActivityId", ActivityId);
                                return Promise.resolve(true);
                              } catch (e) {
                                return Promise.resolve(true);
                              }
                            }}
                          />
                          

                          更加优雅的办法是给onFinish 提交的数据添加一个convertValues

                          前端初学者的Ant Design Pro V6总结(下)
                          (图片来源网络,侵删)
                          const convertValues = useMemo((values: FormColumn) => {
                              return { ...values };
                          }, []);
                          

                          注意:

                          ProForm中的transform和convertValue属性,仅能操作本字段内容,这个特性在某种情况下会出现一些问题

                          例如:

                          'lg'}
                              rules={[{required: true, message: '请选择活动投放时间!'}]}
                              dataFormat={FORMAT_DATE_TIME_CN}
                          /
                          

                          时间范围组件返回的数据格式是

                          ActivityTime: string[] // 如果不给dataFormat,就是 Dayjs[]
                          

                          如果后端接口的数据格式是

                          {startTime: string, endTime: string}
                          

                          这个时候如果使用convertValue无法解决业务问题,需要在onFinish或onSubmit中进行数据转化。

                          EditorTable 可编辑表格

                          提交按钮一直Loading?

                          如果onSave时网络请求错误或者发生异常,返回Promise.reject,onSave就不会生效。

                          if (!activityIdField) {
                          	const errorContent = '请先创建活动';
                          	message.error(errorContent);
                          	return Promise.reject(errorContent);
                          }
                          return handleSaveRow(record);
                          

                          columns 自定义表单、自定义渲染

                           const columns: ProColumns[] = [
                              {
                                  title: '模型文件',
                                  dataIndex: '_File',
                                  width: 150,
                                  render: (_, entity) => {
                                    return (
                                      'link'}
                                        onClick={() = {
                                          downloadFile(entity._File!.originFileObj!);
                                        }}
                                      >
                                        {entity._File?.name}
                                      
                                    );
                                  },
                                  formItemProps: {
                                    valuePropName: 'file',
                                    trigger: 'fileChange',
                                    rules: [{ required: true, message: '此项是必填项.' }],
                                  },
                                  renderFormItem: () => ,
                              }   
                          ]
                          

                          formItemProps 它本质就是,基本照着Form.Item那边去配置就行。

                          form / formRef 的 setFieldValue / getFieldsValue 无效?

                          原因一:

                          由于EditorTable的 Form实际上是新增的一行,是动态的,formRef 更新不及时可能导致formRef.current 为 undefined。

                          原因二:

                          普通的form组件内部的数据模型形如这样:

                          {
                              "homePath": "/",
                              "status": true,
                              "sort": 1
                          }
                          

                          但是editorForm在编辑时内部的数据模型是这样的:

                          {
                              "229121": {
                                  "ModelLoadName": "11",
                                  "ModelShowName": "222",
                                  "ModelNo": "333",
                                  "MobileOS": "android",
                                  "_Position": [
                                      {
                                          "position": [
                                              123.42932734052755,
                                              41.79745486673118
                                          ]
                                      }
                                  ],
                              }
                          }
                          

                          它在外面包了一层,因此设置列的时候需要这么写

                          renderFormItem: (schema, config, form, action) => {
                              const fieldsValue = form.getFieldsValue()
                              const key = Object.keys(fieldsValue)[0];
                              const fields = fieldsValue[key];
                              const fieldName = schema.dataIndex! as keyof typeof fields // you want setting field
                              fields[fieldName] = 'you want setting value';
                              formRef?.current?.setFieldValue(key, fields);
                              return 
                          },
                          

                          Upload / ProUploader 文件上传

                          ImgCrop 实现图片裁切

                          实现功能:

                          • 文件格式限制
                          • 文件上传尺寸限制
                          • 文件缩放大小限制

                            工具函数

                            function getImageFileAsync(file: File): Promise
                              width: number;
                              height: number;
                              aspectRatio: number;
                              image: HTMLImageElement;
                            } {
                              return new Promise((resolve, reject) => {
                                const reader = new FileReader();
                                const img = new Image();
                                reader.onload = () => {
                                  img.src = reader.result as string;
                                };
                                img.onload = () => {
                                  const width = img.width;
                                  const height = img.height;
                                  const aspectRatio = width / height;
                                  resolve({
                                    width,
                                    height,
                                    aspectRatio,
                                    image: img,
                                  });
                                };
                                img.onerror = () => {
                                  reject(new Error('图片加载失败'));
                                };
                                reader.onerror = () => {
                                  reject(new Error('文件读取错误'));
                                };
                                // 读取文件内容
                                reader.readAsDataURL(file);
                              });
                            }
                            

                            组件

                            import { FC, ReactNode, useRef, useState } from 'react';
                            import { message, Modal, Upload, UploadFile, UploadProps } from 'antd';
                            import ImgCrop, { ImgCropProps } from 'antd-img-crop';
                            import { RcFile } from 'antd/es/upload';
                            import { getBase64, getImageFileAsync } from '@/utils/common';
                            const fileTypes = ['image/jpg', 'image/jpeg', 'image/png'];
                            interface PictureUploadProps {
                              // 上传最大数量
                              maxCount?: number;
                              // 文件更新
                              filesChange?: (files: UploadFile[]) => void;
                              // 图片最小大小,宽,高
                              minImageSize?: number[];
                              // 图片裁切组件配置
                              imgCropProps?: Omit;
                              // 上传提示内容文本
                              children?: ReactNode | ReactNode[];
                            }
                            const PictureUpload: FC = ({
                              maxCount,
                              filesChange,
                              minImageSize,
                              imgCropProps,
                              children,
                            }) => {
                              const [previewOpen, setPreviewOpen] = useState(false);
                              const [previewImage, setPreviewImage] = useState('');
                              const [previewTitle, setPreviewTitle] = useState('');
                              const [fileList, setFileList] = useState([]);
                              const [maxZoom, setMaxZoom] = useState(2);
                              const isCropRef = useRef(false);
                              const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
                                setFileList(newFileList);
                                if (filesChange) filesChange(fileList);
                              };
                              const handleCancel = () => setPreviewOpen(false);
                              const handlePreview = async (file: UploadFile) => {
                                if (!file.url && !file.preview) {
                                  file.preview = await getBase64(file.originFileObj as RcFile);
                                }
                                setPreviewImage(file.url || (file.preview as string));
                                setPreviewOpen(true);
                                setPreviewTitle(file.name || file.url!.substring(file.url!.lastIndexOf('/') + 1));
                              };
                              return (
                                
                                  1}
                                    zoomSlider={true}
                                    minZoom={1}
                                    maxZoom={maxZoom}
                                    aspect={minImageSize && minImageSize[0] / minImageSize[1]}
                                    beforeCrop={async (file) = {
                                      isCropRef.current = false;
                                      // 判断文件类型
                                      const typeMatch = fileTypes.some((type) => type === file.type);
                                      if (!typeMatch) {
                                        await message.error(
                                          '图片格式仅支持' +
                                            fileTypes.reduce(
                                              (prev, cur, index, array) => prev + cur + (index === array.length - 1 ? '' : ','),
                                              '',
                                            ),
                                        );
                                        return false;
                                      }
                                      // 判断图片大小限制
                                      if (minImageSize) {
                                        const { width: imageWidth, height: imageHeight } = await getImageFileAsync(file);
                                        if (imageWidth 
                                    fileList}
                                      onPreview={handlePreview}
                                      onChange={(files) = {
                                        handleChange(files);
                                        console.log(files);
                                      }}
                                      maxCount={maxCount}
                                      accept={'.jpg, .jpeg, .png'}
                                      beforeUpload={async (file) => {
                                        if (!isCropRef.current) return Upload.LIST_IGNORE;
                                        return file;
                                      }}
                                    >
                                      {maxCount ? fileList.length  
                            

                            ImgCrop 组件注意事项

                            • 拦截裁切事件

                              • ImgCrop 组件 的 beforeCrop 返回 false 后不再弹出模态框,但是文件会继续走 Upload 的 beforeUpload 流程,如果想要拦截上传事件,需要在beforeUpload 中返回 Upload.LIST_IGNORE。
                              • 判断是否拦截的状态变量需要用 useRef ,useState测试无效。
                              • Upload组件 配合 ImgCrop组件时,一定要在 beforeUpload 中返回 事件回调中的 file,否则裁切无效。

                              • 如果不想做像素压缩,设置quality={1}

                                StepsForm 分布表单

                                如何在 StepsForm 中 更新子表单?

                                通过StepsForm的 formMapRef 属性,它可以拿到子StepForm的全部ref。

                                const stepFormMapRef = useRef([]);
                                return stepFormMapRef} /
                                

                                打印 ref.current

                                [
                                    {
                                        "current": {
                                            // getFieldError: f(name)
                                        }
                                    },
                                    {
                                        "current": {
                                            // getFieldError: f(name)
                                        }
                                    },
                                    {
                                        "current": {
                                            // getFieldError: f(name)
                                        }
                                    }
                                ]
                                

                                如何手动控制 步骤 前进、后退?

                                灵活使用 current、onCurrentChange、submitter属性

                                const [currentStep, setCurrentStep] = useState(0);
                                return (
                                	currentStep}
                                		onCurrentChange={setCurrentStep}
                                		submitter={{
                                          render: (props) = {
                                            switch (props.step) {
                                              case 0: {
                                                return (
                                                  () = props.onSubmit?.()}>
                                                    下一步
                                                  
                                                );
                                              }
                                              case 1: {
                                                return (
                                                  () = props.onSubmit?.()}>
                                                    下一步
                                                  
                                                );
                                              }
                                              case 2: {
                                                return (
                                                  () = {
                                                      setCurrentStep(0);
                                                      onCancel();
                                                    }}
                                                  >
                                                    完成
                                                  
                                                );
                                              }
                                            }
                                          },
                                        }}
                                        stepsProps={{ direction: 'horizontal', style: { padding: '0 50px' } }}
                                	>
                                		{ // StepForm }
                                	
                                )
                                

                                微前端 Qiankun

                                文档:https://umijs.org/docs/max/micro-frontend

                                子应用配置(@umi)

                                一、使用umi创建React App

                                二、配置umi

                                这里有一些WASM的配置,不想要可以去掉

                                import { defineConfig } from 'umi';
                                export default defineConfig({
                                  title: 'xxxxxx',
                                  routes: [
                                    {
                                      path: '/',
                                      component: 'index',
                                    },
                                    { path: '/scene-obj', component: 'OBJScene' },
                                    { path: '/*', redirect: '/' },
                                  ],
                                  npmClient: 'pnpm',
                                  proxy: {
                                    '/api': {
                                      target: 'http://jsonplaceholder.typicode.com/',
                                      changeOrigin: true,
                                      pathRewrite: { '^/api': '' },
                                    },
                                  },
                                  plugins: [
                                    '@umijs/plugins/dist/model',
                                    '@umijs/plugins/dist/qiankun',
                                    '@umijs/plugins/dist/request',
                                  ],
                                  model: {},
                                  qiankun: {
                                    slave: {},
                                  },
                                  request: {
                                    dataField: 'data',
                                  },
                                  mfsu: {
                                    mfName: 'umiR3f', // 默认的会冲突,所以需要随便取个名字避免冲突
                                  },
                                  chainWebpack(config) {
                                    config.set('experiments', {
                                      ...config.get('experiments'),
                                      asyncWebAssembly: true,
                                    });
                                    const REG = /\.wasm$/;
                                    config.module.rule('asset').exclude.add(REG).end();
                                    config.module
                                      .rule('wasm')
                                      .test(REG)
                                      .exclude.add(/node_modules/)
                                      .end()
                                      .type('webassembly/async')
                                      .end();
                                  },
                                });
                                

                                三、跨域配置

                                import type { IApi } from 'umi';
                                export default (api: IApi) => {
                                  // 中间件支持 cors
                                  api.addMiddlewares(() => {
                                    return function cors(req, res, next) {
                                      res.setHeader('Access-Control-Allow-Origin', '*');
                                      res.setHeader('Access-Control-Allow-Headers', '*');
                                      next();
                                    };
                                  });
                                  api.onBeforeMiddleware(({ app }) => {
                                    app.request.headers['access-control-allow-origin'] = '*';
                                    app.request.headers['access-control-allow-headers'] = '*';
                                    app.request.headers['access-control-allow-credentials'] = '*';
                                    app.request.originalUrl = '*';
                                  });
                                };
                                

                                四、修改app.ts,子应用配置生命周期钩子.

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

                                父应用配置(@umi/max)

                                config.ts

                                export default defineConfig({
                                  qiankun: {
                                    master: {
                                      apps: [
                                        {
                                          name: 'r3f-viewer', // 子应用的名称
                                          entry: 'http://localhost:5174', // your microApp address
                                        },
                                      ],
                                    },
                                  },
                                })
                                

                                使用路由的方式引入子应用

                                export default [
                                  {
                                    name: 'slave',
                                    path: '/slave/*',
                                    microApp: 'slave',
                                    microAppProps: {
                                      autoSetLoading: true,
                                      autoCaptureError: true,
                                      className: 'MicroApp',
                                      wrapperClassName: 'MicroAppWrapper'
                                    },
                                  },
                                ]
                                

                                使用组件的方式引入子应用

                                index.tsx

                                import { PageContainer } from '@ant-design/pro-components';
                                import { memo } from 'react';
                                import { MicroAppWithMemoHistory } from '@umijs/max';
                                import './index.less';
                                const Role = () => {
                                  return (
                                    
                                      true}
                                        className={'microApp'}
                                      /
                                    
                                  );
                                };
                                export default memo(Role);
                                

                                index.less

                                .microApp,
                                #root {
                                  min-height: 800px !important;
                                  height: 800px !important;
                                  max-height: 800px !important;
                                  width: 100% !important;
                                }
                                
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码