axios 重发请求后数据如何重新渲染

301 天前
 bhbhxy

由于 token 过期,在获取新 token 后使用 axios 重新发送之前的请求,数据也传回来了,但是现在有一个问题,列表数据无法显示了,页面的结构是这样的 index.vue

<template>
	<ChildComponent />
</template>

我是在<ChildComponent />中请求的数据:

	<template>
    	<ul>
        	<li v-for="item in list">{{item}}</li>
        </ul>
    </template>
    <script>
		const list = getDataFromApi().data
    </script>

重发请求写在拦截器里:

axios.interceptors.response.use(
	response => {
		//请求重发
        if(statusCode === 401) {
        	getNewToken()
			return axios.request(response.config);
        }
	}	
})

请求是重发了,但是组件里的list并没有接收到新数据,因此列表也没有重新渲染,请问这种情况怎么处理,或者有什么别的解决方案吗

2726 次点击
所在节点    Vue.js
36 条回复
shakukansp
301 天前
首先你这个 list 不是响应式数据, 要用 ref 包一下
看下 vueuse 里面的 useAxios 的思路,或者 useFetch ,或者看下 vue-request
lisongeee
301 天前
```ts
const list = ref([])
getDataFromApi().then(data=>{
list.value = data
})
```
rain0002009
301 天前
axios 拦截器是可以返回 promise 的 所以用 async await 改造一下不就好了
bhbhxy
301 天前
@shakukansp 这是为了方便起见随手敲的代码,原始代码是 const list = reactive({list:[]})
shakukansp
301 天前
@bhbhxy 你这自然不行

const list =
既然是 const 那么就该意识到这个变量的引用地址不能修改,改的话直接报错

那你改 reactive 里面的 list 为啥不报错,说明改了以后 reactive 里面的 list 和外面 const 声明的 list 不是指向同一个内存地址了
bhbhxy
301 天前
@shakukansp 不关注语法层面,我只是随手写了一下,ChildComponent 在页面初始化时如果 token 有效,会返回{code:0, data: [......], msg:""},此时是展示数据的,如果 token 失效,会返回{code:-1, data:null, msg:'token invalid'},此时不展示数据,现在使用 axios 重发请求,只是重发,重发获取的数据怎么塞到这个组件的 list 里,用 prop 传值 然后 watch 监听?还是有更专业的做法,这样是不是意味着我要把组件自身维护的数据都提取到外部窗器中来
return axios.request(response.config) 重发了请求,这个代码写在全局拦截器里,组件内是不知道重新发起了一次请求的,所以 list 还是原来的空值
yetrun
301 天前
示例代码还是得认真写一下,不然看的人会产生歧义。我举个例子:

getNewToken() // 这个是异步吗?
return axios.request(response.config);

如果是异步,整个代码结构就不对了,改成下面的格式:

await getNewToken()
return await axios.request(response.config)
bhbhxy
301 天前
@yetrun 不关注语法层面,伪代码只是为了方便看出代码逻辑。
我在封装好的 request.js 写了 axios 的拦截器:
service.interceptors.response.use(
async error => {
if(statusCode === 401) {
await getNewToken();
return service.request(error.response.config) //重发请求
}

return Promise.reject(error);
}
)

而我在组件内获取数据是这样写的:
fetchData() {
//调用接口获取数据
//给响应式变量 list 赋值
}
onMounted(() => fetchData())

在 token 有效的情况下,数据正确展示,如果 token 无效,就先到代码片段一中获取新 token 再发一次请求,这个请求获取到新数据后,怎么赋值给组件内的 list
yetrun
301 天前
@bhbhxy 你的逻辑没问题,只能关注代码层面了。细节导致 Bug. 我只是举个例子,其他的地方你要注意。这个是 Debug ,不是帮你判断逻辑有没有问题。这个也不是伪代码,而是省略的代码。省略的代码可以,但突出的地方一定要突出,就像我上面提到的,该 await 的地方一定要 await ,否则整个结构就说不通了会导致错误。

按照你现在的代码格式,大体上是没问题了。不过既然你用到了异步 async 了,那么不要再返回 Promise 了,使用 await 保持一致性:

return await service.request(error.response.config)
return await Promise.reject(error)

如果还有问题,可能在这:

return await service.request(error.response.config) // 新获取的 Token 写进去了吗?
bhbhxy
301 天前
@yetrun 发送新的 token 后数据已经获取到了,但这个操作只是重发了请求,并没有给组件内的 list 赋值,因为这一步是在组件外发起的请求,所以应该怎么赋值给组件内的 list
yetrun
301 天前
@bhbhxy 你的问题是因为 token 过期导致 list 赋值失败,还是单纯地想问如何给 list 赋值这个问题?如果 token 没过期,list 赋值正常吗?
bhbhxy
301 天前
@yetrun token 有效的情况下,list 赋值正常,token 过期后由于是在拦截器层面重发的请求,导致组件内部的 list 没有感知到数据的变化,怎么给重发请求后的 list 赋上值
yetrun
301 天前
@bhbhxy 我检查了一下 axios 拦截器的文档,发现确实和你的写法有点出入:

axios.interceptors.response.use(function (response) {
// 正常请求
return response;
}, async function (error) {
// 异常请求
if(statusCode === 401) {
await getNewToken()
return await axios.request(response.config);
} else {
return await Promise.reject(error);
}
});

要写在第二个参数。
bhbhxy
301 天前
@yetrun 不是语法问题,不然编译阶段都得报错,只是为了节省篇幅省略了这一部分
bhbhxy
301 天前
@yetrun onMounted(() => fetchData())组件获取数据在页面初始化时只执行一次,在 token 失效的情况下,这个请求返回的是 401 未授权,所以由拦截器获取新 token 后重发一次请求获取正确数据,这时候 onMounted 里的方法不会再执行了,也就是没有机会给 list 赋值。重发请求是指再请求一遍接口的 url ,而不是再执行一次组件内的 fetchData
yetrun
301 天前
@bhbhxy 我没说语法问题,说的是用法问题。你仔细看下我的回答啊。
zzzzhan
301 天前
@bhbhxy #8 return service.request(error.response.config) 重发的请求进入到 axios 的拦截器了吗
bhbhxy
301 天前
@zzzzhan 获取新 token 后就是正常接口数据了,是走的正常 response 拦截器,token 失效走的 error
shakukansp
301 天前
@bhbhxy 一串楼看下来,你还是需要看我 1 楼说的那几个库
shakukansp
301 天前
简单地说就是你需要把请求的结果转成响应式变量

const { data: tableData, loading, send: refetch } = useAxios(axios.get('xxxxx')) 类似这种东西

这里面的 data 本身就是个响应式变量,useAxios 内的 async 函数每次运行都会更新这个 data, 你把这个 data 直接当 list 用或者 computed 处理一下格式

send 是手动重新触发请求

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/956106

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX