思路:
- 在 token 即将过期前主动刷新(微信小程序文档中的推荐方式)
- 请求前刷新token,避免使用队列保存axios请求配置,减少复杂度
- 使用一个全局promise, 避免多接口失效时,refreshToken 多次执行
// axios.ts
import axios, { AxiosError } from 'axios'
import { router } from './App'
let promise = Promise.resolve()
axios.interceptors.request.use(async function (request) {
await refreshToken()
const token = localStorage.getItem('token') || ''
if (request.headers != null) {
request.headers['token'] = token
}
return request
})
function refreshToken() {
// 上一次的 token 更新时间, login | refreshToken
const lastUpdate = Number(localStorage.getItem('lastUpdate'))
// 24 小时主动刷新一次token
if ((lastUpdate && Date.now() - lastUpdate < 1000 * 24 * 60) || !lastUpdate) {
return
}
// 使用一个新的axios实例,避免走到拦截器里导致死循环
const instance = axios.create({
baseURL: BASE_URL,
})
// 请求之前 或者 请求结束(无论成功失败) 更新 lastUpdate
localStorage.setItem('lastUpdate', String(Date.now()))
// 经典的 promise = promise.then 代码, 来控制同一时刻仅有一个refreshToken在执行
promise = promise.then(() => {
return instance
.get('/refreshToken', {
params: {
refresh_token: localStorage.getItem('refreshToken'),
},
})
.then(async (res) => {
localStorage.setItem('token', res.data.data)
return Promise.resolve()
})
.catch((err) => {
router.navigate('/login')
return Promise.reject('refreshToken失效,重新登录')
})
})
return promise
}
// App.tsx
import {
createBrowserRouter,
RouterProviderProps,
} from 'react-router-dom'
export let router: RouterProviderProps['router']
useEffect(async ()=>{
const routes = await fetchRoutes()
router = createBrowserRouter(routes)
},[])
此外,在登录之后,需要执行一下 localStorage.setItem('lastUpdate', String(Date.now()))