网站新闻打不开,厦门网站建设680,动漫网页设计版式,手机端网站ui做多少像素1. 背景及目的
1.1 需求背景
随着应用的拆分#xff0c;目前子应用有12个#xff0c;这些子应用都使用的是同一个request实例。
前端支持后端切流#xff0c;增加多个拦截器用于灰度
经手动梳理#xff1a;
目前所有应用中有26个在使用的拦截器#xff0c; 其中用于灰…1. 背景及目的
1.1 需求背景
随着应用的拆分目前子应用有12个这些子应用都使用的是同一个request实例。
前端支持后端切流增加多个拦截器用于灰度
经手动梳理
目前所有应用中有26个在使用的拦截器 其中用于灰度切流的拦截器有13个 Request 16个 Response 10个
各个模块使用到的拦截器及是否可以下线的详情看所有应用中拦截器使用情况
1.2 现状分析 及 问题
根据背景我们发现目前 request为单实例这个request实例在主应用创建并添加到window.APP_CONTEXT中暴露给子应用获取所有应用使用的都是同一个request实例。
且 interceptor修改没有加限制可能会造成下列的问题
1.2.1 增加接口rtt耗时
前端为了支持后端接口切流使用了20个拦截器去做灰度处理每个接口的请求都要经过20个拦截器逻辑增加接口请求、响应耗时。
1.2.2 范围模糊
这些拦截器有些写在主应用有些写在子应用即使接口只需要子模块范围内拦截的也会作用到所有模块。
1.2.3 不可控
随着应用的拆分目前1个主应用12个子应用使用的都是同一个request实例。
任一应用对request的修改会影响其他所有应用且其中有部分应用是跨团队在维护增加了request的不可控性。
1.3 需求目的
梳理现状整理现有 interceptors做好归类以及标志为后续的解决方案提供参考材料探索有效解决 Request-Intercepor 现有问题、可行性较高 且 改造成本低的优化方案
2. 整体设计
通过源码阅读得知Interceptor 的绑定及执行逻辑
2.1 目前request绑定interceptor情况
2.2.1 主应用初始化过程中生成Request实例之后
在main.js 加载时调用.
2.2.2 子应用初始化过程中
在lazy-module中调用拦截器注册函数
在initModule中MF提供的afterRegisteration所有子模块注册后与beforeRegisteration所有子模块注册前方法中调用.
2.2.3 子应用实际发起请求时
根据使用情况use拦截器
无论什么时机调用拦截器影响的是挂载拦截器拦截器生效时机最终还是作用于全局。
2.2 目前的request实例可能会出现的interceptor使用情况
a.主应用内使用主应用内定义的interceptor
b.子应用调用子应用内部的interceptor
c.子应用调用主应用定义的interceptor
c.子应用间互相调用interceptor
2.3 目标 优化后request使用情况
2.3.1 Request 实例独立
加载时机
主应用主应用初始化使用主应用生成request实例的方法生成request实例。request实例挂载interceptor
子应用进入子模块a拿到只属于本模块requestA实例。requestA实例挂载interceptor.
卸载时机
无需卸载离开子模块a进入子模块b后使用的是子模块b的拦截器。
2.3.2 Interceptor 独立
a. 主应用内使用主应用内定义的interceptor
b.子应用调用子应用内部的interceptor
c.子应用可以调用主应用共享出来的interceptor私有的无法访问
d. 子应用间可以实现互相调用interceptor
3. 详细设计
3.1 思路一 私有化request实例
提供生成request实例的方法使各子应用只能基于主应用提供的方法生成自己应用内的实例。子应用间request实例互不影响子应用只能修改模块内部的interceptor
私有变量方式提供生成request实例的方法使各子应用只能基于主应用提供的方法生成自己应用内的实例
主应用
import axios from axios;// 通过内部变量
let _instance null;const baseURL ;
const defaultConfig {baseURL,
};class Request {constructor() {_instance axios.create(defaultConfig);// 全局 interceptor_instance.interceptors.request.use(() {},() {});_instance.interceptors.response.use(() {},() {});}async doRequest(config) {console.log(config.method);try {const response await _instance(config);return response;} catch (e) {// error report}}get() {return this.doRequest({method: get,});}post() {return this.doRequest({method: post,});}// 子模块通过主应用 Request 实例 提供的方法进行新的 Request 孵化incubation(config) {const request axios.create({...defaultConfig,...config,});// 拷贝全局 interceptorconst baseRequestInterceptor _instance.interceptors.request.handlers || [];baseRequestInterceptor.forEach((i) {request.interceptors.request.use(i.fulfilled, i.rejected);});const baseResponseInterceptor _instance.interceptors.response.handlers || [];baseResponseInterceptor.forEach((i) {request.interceptors.response.use(i.fulfilled, i.rejected);});return request;}
}Request.getInstance function () {if (!this.instance) {this.instance new Request();}return this.instance;
};export const request Request.getInstance(); /*
* -----------
* expose function
* -----------
*/
export const NewRequest Request.incubation;
子应用
/** -----------* call expose function NewRequest to get a new request instance* -----------
*/
const request getAppCtx().NewRequest();子模块不能添加全局 interceptor子应用只能修改模块内部的interceptor 删除现有expose到挂载在window下的APP_CONTEXT中的request实例子应用无法直接操作主应用的request实例
基于以上思路 进行修改经验证方法可行
主应用页面中子应用的interceptor没有执行。
子应用中子应用中绑定的interceptor和主应用分享出来的interceptor被执行.
子应用间交叉依赖
由于是多实例可能会出现子应用a会调用子应用b中的业务接口。若该业务接口在b中有拦截器对之进行处理子应用a也需要使用该拦截器。
上面的修改无法兼容子应用间交叉依赖的场景。
为了达到子模块只能修改本模块的request interceptor但又要互通对此可以考虑通过主应用生成对应模块的request只派发该模块的request实例。通过map[module]requestInstance的方式存储管理。
若子模块a需要使用子模块b的interceptor则子应用告诉主应用需要哪个模块的request实例由主应用派发模块b的request实例给模块a使用。如上图所示
子应用间交叉依赖的场景比较少见且是不规范行为此方案只为兼容特殊场景。
为兼容上述场景将做下面的调整
基于当前已有的逻辑通过主应用以私有变量方式生成每个模块的实例。当模块修改其request实例时主应用中的moduleRequest管理每个module对应的request实例
主应用
Request.incubation function (module) {
/*
* 当需要用到其他模块的reuqest时直接返回已有的request实例
*/
if(this.moduleRequest[module]) return this.moduleRequest[module];const mainRequest Request.getInstance();const instance new Request();const baseRequestInterceptor (mainRequest.axiosInstance.interceptors.request mainRequest.axiosInstance.interceptors.request.handlers) || [];baseRequestInterceptor.forEach((i) {instance.getAxiosInstance().interceptors.request.use(i.fulfilled, i.rejected);});const baseResponseInterceptor (mainRequest.axiosInstance.interceptors.response mainRequest.axiosInstance.interceptors.response.handlers) || [];baseResponseInterceptor.forEach((i) {instance.getAxiosInstance().interceptors.response.use(i.fulfilled, i.rejected);});this.moduleRequest[module] instance;return this.moduleRequest[module];
};
export const NewRequest Request.incubation;子应用使用
const request getAppCtx().NewRequest(instation);
const requestOtherModule (otherModule) getAppCtx().NewRequest(otherModule);//业务中用到时才调用防止获取时早于模块的初始化当子模块正常使用时
request.get(/api/a-scope/...)当子模块需要依赖其他模块时
requestOtherModule(b).get(/app-b/...)3.2 思路二共用request同一实例通过来源分发拦截器
各域给request传入自己的config标识主应用通过判断标识区分来源
通过interceptors.xx.use中提供的options runWhen 获取子应用传入request的标识判断是否需要绑定拦截器到request实例中。 然而 添加runWhen属性需要开发的自觉性可以被跳过
且 项目中的axios版本没有升级没有新加的synchronous与runWhen属性若需要使用要考虑升级版本. 项目中的axios包代码
通过来源加载、卸载拦截器
基于 目前已有可以获取当前页面来源于哪个模块的支持。
3.3 思路三改造Axios
将axios执行拦截器的方式由推进队列改为用map对应key执行key中的拦截器。key为子应用标识。
方案权衡
思路优劣势思路一 私有化request实例子应用间request实例互不影响优势满足各域拦截器私有化问题满足各域request独立管理目的。劣势多实例下子应用间互相依赖时使用者需要额外说明多实例风险较高可能会存在没有意识到的问题思路二共用request同一实例通过来源分发拦截器优势 满足各域拦截器私有化问题逻辑简单修改起来方便。劣势只解决了拦截器绑定的问题没有解决request的独立管控问题目前项目中的axios没有支持runWhen需要查看依赖包版本思路三改造Axios优势满足各域拦截器私有化问题 。劣势改造成本较高不满足各域request独立管理目的