虽然说天天看到各种面试题后端返还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
}