后端一次性返还10条数据,要求前端分页....

18 阅读3分钟

虽然说天天看到各种面试题后端返还1w条数据的情况,我觉得会有这种场景吗,这次终于让我遇到了

因为需要把整段数据全部给计算好,分页返还相对麻烦,一次返还方便计算,于是乎分页任务就留给前端

解决思路

实际上解决方式根据不同场景有不同方案,如果我一个个页面去修改那么总共有100来个页面需要进行修改,那样耗时费力,于是乎在axios拦截器层面进行修改成为了最优解

1.初使思路

刚开始想着做一层缓存,第一次查询写入到map里面,后续查询在拦截器层面进行响应取消,后拿取map里面的数据返回出去

const service = axios.create({
 
  timeout: 10000,

})
const map = new Map()

// request拦截器
service.interceptors.request.use(
  (config) => {
  //添加拦截器
    const abort = new AbortController()
    config.signal = abort.signal
     if(针对想要缓存的接口判断){
      const cache =  map.get(存入的id)
       if(cache){
        abortController.abort()
        return cache
       }
         
     }
    return config
  },
  (error) => {
    console.log(error)
    Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  (res) => {
 
     return res.data
    }
  },
  (error) => {
      if (针对想要缓存的接口判断) {
        map.set(`${res.config.method}-${res.config.url}`, res)
      }
    return Promise.reject(error)
  }
)

该方案确实能做到拦截请求与缓存数据,但拦截请求走的是response拦截器的err函数,这一点上并不符合需求

正确方式

在axios中有个adapter属性,这是自定义请求方式。总所周知,axios在浏览器上底层是基于xhr,node是http 具体代码是在axios库当中的./lib/adapters

这也意味着你可以自定义使用fetch发送请求以满足需求。

具体封装思路就变为查询到mao缓存就不发请求,查询不到就发起请求。那是否意味着我们需要在写一遍xhr呢?

不需要,axios有个axios.defaults.adapter,这就可直接发送请求,于是代码就是

const service = axios.create({
  timeout: 10000,
  adapter: customAdapter(axios.defaults.adapter),
})
const map = new Map()
function customAdapter(adapter) {
  return (config) => {
     const cache = map.get(`${config.method}-${config.url}`)
     if (判断走缓存) {
       console.log(config)
       console.log(cache)
       return Promise.resolve(cache)
     }
    //走请求
    return adapter(config)
  }
}

详情可查看./lib/adapters/README.md文档

这样既可满足需要

后续问题补充

问题1:关于结果深拷贝问题

// 响应拦截器
service.interceptors.response.use(
  (res) => {
    // 未设置状态码则默认成功状态
    const code = res.data.code || 200
    // 获取错误信息
    const msg = errorCode[code] || res.data.msg || errorCode['default']
    // 二进制数据则直接返回
  

        map.set(`${res.config.method}-${res.config.url}`, res)
 
         res.data.data = {
          list: res.data.data.slice((res.config.params.pageNo - 1) * 10, res.config.params.pageSize * (pageNum + 1)),
          total: res.data.data.length,
        }

      return res.data
    }
  },
  (error) => {
  
    return Promise.reject(error)
  }
)

当这样直接设置时,如果底下进行取值,会修改map原值。核心原因是浅拷贝。

解决办法使用 JSON.parse(JSON.stringify(res)) 进行深拷贝

问题2:关于请求参数修改不再取缓存。

在customAdapter中,如果遇到非pageNo与pageSize修改时,需要重新请求接口获取缓存于是需要进行判断新请求的params/data参数是否会有

function customAdapter(adapter) {
  return (config) => {
    if (config.params && map.has(`${config.method}-${config.url}`)) {
      // 判断除去pageNo与pageSize参数,其他是否相同
      const isDelete = determineSame(config)

      if (!isDelete) map.remove(`${config.method}-${config.url}`)
    }

    const cache = map.get(`${config.method}-${config.url}`)
    if (config.params && cache) {
      console.log(cache.config.params)
      cache.config.params = config.params
      return Promise.resolve(cache)
    }
    return adapter(config)
  }
}
// 判断是否删除缓存
function determineSame(config) {
  const cache = map.get(`${config.method}-${config.url}`)
  for (const key in config.params) {
    if (key === 'pageNo' || key === 'pageSize') continue
    if (config.params[key] !== cache.config.params[key]) {
      return false
    }
  }
  return true
}