来源:得物技术
目录
一、背景
二、大仓下组件共享方式
1. 源码引入组件
2. MF 远程组件
三、最佳实践
1. 业务权限控制
2. 埋点上报
3. 降级方式
四、源码依赖结合 MF 模式
1. 先源码引入后 MF
2. 先MF后源码引入
五、未来&总结
1. 未来
2. 总结
一
背景


二
大仓下组件共享方式
源码引入组件
组件提供
// 区分普通组件,新增一个remote-components组件目录cd remote-components && dx cc order-detail


组件使用
workspace:*,构建时动态去取_share/目录下最新版本的组件资源。若从稳定性考虑,也可以固定版本号。
/** package.json */"@demo/order-detail": "workspace:*"/** 业务组件 */import OrderDetail from '@demo/order-detail'<OrderDetail {...props} />



总结
MF远程组件
@umijs/max导出defineConfig,也可以使用@umijs/plugins/dist/mf插件去支持配置 MF 属性,本质也是对 WebPack Plugin 的封装,属性是类似的。不一样的点在于 Host 不再需要通过配置 Exposes 将组件一个个的暴露出去,而是约定暴露 Exposes 目录下的组件,十分方便。
/** 方法一:使用umijs/max导出的defineConfig */import { defineConfig } from '@umijs/max';export default defineConfig({// 已经内置 Module Federation 插件, 直接开启配置即可mf: {remotes: [{name: `remote${MFCode}`,aliasName: 'APP_A',entry: 'xxx/remote.js',},],// 配置 MF 共享的模块shared,},});
/** 方法二:使用umijs/plugins/dist/mf的插件 */import { defineConfig } from 'umi';export default defineConfig({plugins: ['@umijs/plugins/dist/mf'], // 引入插件mf: {remotes: [{name: `remote${MFCode}`,aliasName: 'APP_A',entry: 'xxx/remote.js',},],// 配置 MF 共享的模块shared,},});
组件提供

之前

现在
组件使用
umijs/max的safeRemoteComponent 异步注册组件。
//config.tsconst APP_A_ENTRIES = {PROD: 'https://prod-a-env.com/xxxx/remote.js',DEV: 'https://dev-a-env.com/xxxx/remote.js',PRE: 'https://pre-a-env.com/xxxx/remote.js',TEST: 'https://test-a-env.com/xxxx/remote.js',}const APP_B_ENTRIES = {PROD: 'https://prod-b-env.com/xxxx/remote.js',DEV: 'https://dev-b-env.com/xxxx/remote.js',PRE: 'https://pre-b-env.com/xxxx/remote.js',TEST: 'https://test-b-env.com/xxxx/remote.js',}mf: {name: `remote${DemoCode}`,library: { type: 'window', name: `remote${DemoCode}` },remotes: [{/** app-A远程组件 */name: `remote${aMFCode}`,aliasName: 'appA',keyResolver: getEnv(),entries: ORDER_ENTRIES,},/** app-B远程组件 */{name: `remote${bMFCode}`,aliasName: 'appB',keyResolver: getEnv(),entries: IM_ENTRIES,},],shared},
${aliasName}和 Host Exposes 目录下的组件名。
总结
三
最佳实践
业务权限控制
系统权限原理(401)


业务权限原理(432)



Request方案
//config.tsexport default defineConfig({// 其他配置proRequest: {},})//app.tsxexport const proRequest = {prefix: proxyFix,envConfig: {},headers: {backstageCode,},successCodes: [200, '200'],};
//utilsimport { AxiosRequestConfig, request } from '@umijs/max';import initCrudApiClass from '@/utils/api';const CrudService = initCrudApiClass<AxiosRequestConfig>(({ url, ...config }) =>request(url as string, config).then((res) => res.data),);CrudService.registerApiOptions('default', {mapping: {paramsType: {read: 'data',remove: 'data',queryList: 'data',queryPage: 'data',},},});
// app.tsxexport const request: RequestRuntimeConfig = {baseURL: proxyFix,// 请求拦截器requestInterceptors: [(c: RequestConfig) => {/** 一些配置 */Object.assign(c.headers, {/** 其他配置 */backstageCode,});return c;},],//响应拦截器responseInterceptors: [(res) => {/** 一些配置 */return res;},],// 错误配置errorConfig: {errorHandler: (error) => {return errorhandlerCallback(error as ResponseError);},},};
"@xxx/utils": "workspace:*"通过请求配置拦截器去新增headers,会自动获取backstageCode,支持传递去修改// src/app.tsximport { RuntimeConfig } from '@umijs/max';/*** @param instance - axios 实例,采用原生方式进行配置即可* @param setOptions - 配置函数*/export const configRequest: RuntimeConfig['configRequest'] = (instance, setOptions) => {instance.interceptors.request.use((c) => {// 默认携带了两个请求头:accessToken、backstageCodeObject.assign(c.headers as object, {backstageCode,});return c;});setOptions({errorResponseHandler(error) {return undefined;},});};
组件双方的 Request 不一致怎么解决
// 可以通过动态注册的方式初始化request,使用UmiRequest.requestInit方法。//被用作远程组件时,从远端拿到系统码,通过api改写headers配置enum BackstageCode {APP_A: 'CODE_A',APP_B: 'CODE_B',APP_C: 'CODE_C'}UmiRequest.requestInit({prefix: proxyFix,headers: {backstageCode: BackstageCode[props.code],},});
// config.ts mf: { name: `remote${mfName}`, library: { type: "window", name: `remote${mfName}` }, shared: { /** 其他依赖 */ '@du/umi-request': { singleton: true, eager: true, } } }权限管控最佳实践



埋点上报


降级方式
import { safeRemoteComponent } from '@umijs/max';import { Spin } from 'poizon-design';import { SharedOrderDetail } from '@xxx/order-detail'import React from 'react';const MFOrderDetail = safeRemoteComponent<React.FC<Props>>({moduleSpecifier: 'Demo/OrderDetail',/** 将源码依赖的组件 */fallbackComponent: <SharedOrderDetail {...props} />,loadingElement: <Spin></Spin>,});const OrderDetailModule: React.FC<Props> = (props) => <MFOrderDetail key={props.name} {...props} />export default OrderDetailModule;

四
源码依赖结合MF模式
先源码引入后MF


先MF后源码引入

五
未来&总结
未来
结合主干研发模式
import FWIns from '@/config/fw-config';const fw = FWIns.init({branchName: 'feature-base-main-xxx-xxx',});await fw.feature(async () => {/** 新逻辑,使用MF*/<MFComponent />},async () => {/** 老逻辑,使用源码依赖*/<SharedComponent />},);
需要开发一些插件
总结
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70027824/viewspace-3007404/ ,如需转载,请注明出处,否则将追究法律责任。