解决复用页面只是接口不同的问题的完整指南

🔥 前言

最近遇到一个需求,需要复用原来的组件页面(页面比较复杂涉及很多弹窗操作,表单和二级详情页),新旧页面组件只是调用接口不同,新旧两套接口,其他只是略微文案上的差异。后期的话新页面肯定会有较大变动,业务着急所以临时复用旧页面。

🤔 问题描述

由于后面可能会有较大变动,这期新加复用页面也可能会下掉,如何有效的修改,并且确保后续下掉这期功能不会影响旧原来功能;

如何能做到更方便下掉这期功能删除代码,且不会影响原来的功能逻辑?

💡 解决方案

假设新增的组件是B,需要引用原来的组件A。

方案一:使用useContext

代码示例

javascript 复制代码
const apiMapA = {
    list : 'xxxx/A/list',
    query: 'xxxx/A/query',
    edit: 'xxxx/A/edit',
    ...
}

 const apiMapB = {
    list : 'xxxx/B/list',
    query: 'xxxx/B/query',
    edit: 'xxxx/B/edit',
    ...
}

Type ApiMapType = typeof apiMapA | typeof apiMapB

const context = useContext<ApiMapType|null>(null)

const APIMapProvider:FC<{value:ApiMapType}> = ({value,...props})=>{

    return <Context.Provider value={value}>
        {props.children}
    </Context.Provider>
}

const useContextAPIMap = ()=>{
    const context = useContext()
        
     
     if(!context){
         return message.error('useContextAPIMap 必须包裹在 APIMapProvider下')
     }
   
   return context
}
javascript 复制代码
const B = ()=>{
    
   return <APIMapProvider value={apiMapB}>
       <A/>
   </APIMapProvider>
}
javascript 复制代码
 const A = ()=>{
 
     return <ContextProvider value={apiMapA}>
        <SubApp/>
    </ContextProvider>
 }

叶子组件中调用接口

javascript 复制代码
const SubApp = ()=>{
   const apiMap = useContextAPIMap()
   
   const handleQuery =()=>{
       ....
       const http.get(apiMap.query)
   
   }

    return <>
       {....}
       <Button onClick={handleQuery}>查询</Button>
   </>
}

优点:

  • 优点1: 文件夹隔离组件范围,删除比较方便,删除B组件文件夹,不会影响A组件内容;
  • 优点2: 每套api对象定义链路没有动态路径,后面IDE追踪引用api地方,且api调用不会出错;

缺点:

  • 缺点1:叶子组件的每处api调用的地方都需要修改成取出useContextAPI去调用,改动量大,比较麻烦;
  • 缺点2:后须删除新增逻辑,想要复原原来A组件的直接调用api方式比较麻烦;

方案二:http使用拦截器

代码示例

由于两套接口的具有特殊规律,所以可以用拦截器去替换请求,需要替换时候正则必须要严格,需要特别小心:

比如:https://www.jsfund.cn/A/query 替换:https://www.jsfund.cn/B/query 这里替换思路:应该是路径开头是/A/的请求;不能简单写错包含A替换成B就行;

另外,由于拦截器是全局的作用范围,所以需要在离开页面的时候去卸载掉拦截器。

最后,内部的新旧文案的判断,可以使用useRouteMatch 或者useSWR做swr缓存去区分。

javascript 复制代码
const B = ()=>{
    // 入口处写入区分页面模块缓存
    useModuleType('B')
    
    useEffect(()=>{
        const id = axios.interceptors.request.use((response)=>{
            
            return response.url.replace(/(https:\/\/\w+)\/A\// ,'$1/B/')
        })
        
        return ()=>{
            axios.interceptors.request.eject(id)
        }
    
    },[])
    
    return <>
            <SubApp/>
        </>
}

自定义指定fetcher函数获取缓存结果,由于是手动写入缓存值,不需要开启验证功能;

javascript 复制代码
    const useModuleType = (moduleType: 'A' | 'B') =>{
        
        // swr key随便指定唯一就行,
       const {data} =  useSWR('_pageB_ModuleType',{
            fetcher: ()=>Promise.resolve(moduleType)
            autorun:false,
            refreshInterval: false,
            revalidateOnFocus: false,
            revalidateIfStale: false
        })
        
        return data
    }

优点:

  • 优点1:对比方法一,修改量极小,不用修改每处api调用的地方;
  • 优点2:后面恢复A模块非常方便,删除代码比较少;

缺点:

  • 缺点1:依赖替换api接口之间有比较明显的逻辑规律;
  • 缺点2:容易错误过滤替换别的模块接口,需要严格限制api接口替换规则;

📝 总结

最终我还是使用方案二,因为这次需要接口替换规则明显,用拦截器非常方便。如果接口替换规则无任何规则的话可能就比较麻烦了。抛砖引玉,如果大家有更好的方案欢迎留言评论😊。

相关推荐
苏格拉没有底了33 分钟前
由频繁创建3D火焰造成的内存泄漏问题
前端
阿彬爱学习35 分钟前
大模型在垂直场景的创新应用:搜索、推荐、营销与客服新玩法
前端·javascript·easyui
橙序员小站1 小时前
通过trae开发你的第一个Chrome扩展插件
前端·javascript·后端
Lazy_zheng1 小时前
一文掌握:JavaScript 数组常用方法的手写实现
前端·javascript·面试
是晓晓吖1 小时前
关于Chrome Extension option的一些小事
前端·chrome
MrSkye1 小时前
🔥从菜鸟到高手:彻底搞懂 JavaScript 事件循环只需这一篇(下)
前端·javascript·面试
方佑1 小时前
✨ Nuxt 混合渲染实践: MemOS前端体验深度优化指南
前端
爱编程的喵1 小时前
React 19 + Vite 6 构建现代化旅行应用智旅(1)
前端·react.js
l1t1 小时前
使用流式函数解决v语言zstd程序解压缩失败问题
前端·压缩·v语言·zstd
小离a_a1 小时前
el-tree方法的整理
前端·vue.js·elementui