使用vue3+ts+axios+pinia实现无感刷新

418 阅读2分钟

使用vue3+ts+axios+pinia实现无感刷新

在 Vue 3 项目中,结合 TypeScript、Axios 和 Pinia 实现 无感刷新(Token 自动刷新) 是一种常见的需求。无感刷新的核心逻辑是:当用户的访问令牌(Access Token)过期时,自动使用刷新令牌(Refresh Token)获取新的访问令牌,而用户无需重新登录。

以下是实现无感刷新的详细步骤和代码示例。

  1. 项目初始化

确保项目已经安装以下依赖:

  • Vue 3

  • TypeScript

  • Axios

  • Pinia

  1. 创建 Pinia Store

使用 Pinia 管理用户的认证状态和令牌。

创建 Auth Store

// src/stores/authStore.ts
import { defineStore } from 'pinia';
import { ref } from 'vue';

export const useAuthStore = defineStore('auth', () => {
  const accessToken = ref<string | null>(null);
  const refreshToken = ref<string | null>(null);

  // 设置令牌
  const setTokens = (newAccessToken: string, newRefreshToken: string) => {
    accessToken.value = newAccessToken;
    refreshToken.value = newRefreshToken;
  };

  // 清除令牌
  const clearTokens = () => {
    accessToken.value = null;
    refreshToken.value = null;
  };

  return {
    accessToken,
    refreshToken,
    setTokens,
    clearTokens,
  };
});
  1. 配置 Axios 实例

创建一个 Axios 实例,并添加请求拦截器和响应拦截器,用于自动刷新令牌。

// src/utils/axiosInstance.ts
import axios from 'axios';
import { useAuthStore } from '@/stores/authStore';

const axiosInstance = axios.create({
  baseURL: 'https://api.example.com',
});

// 请求拦截器:添加 Access Token
axiosInstance.interceptors.request.use((config) => {
  const authStore = useAuthStore();
  if (authStore.accessToken) {
    config.headers.Authorization = `Bearer ${authStore.accessToken}`;
  }
  return config;
});

// 响应拦截器:处理 Token 过期
axiosInstance.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;

    // 如果响应状态码为 401 且未重试过
    if (error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;

      const authStore = useAuthStore();
      if (authStore.refreshToken) {
        try {
          // 使用 Refresh Token 获取新的 Access Token
          const response = await axios.post('/auth/refresh', {
            refreshToken: authStore.refreshToken,
          });

          // 更新 Access Token
          authStore.setTokens(response.data.accessToken, authStore.refreshToken);

          // 重试原始请求
          originalRequest.headers.Authorization = `Bearer ${response.data.accessToken}`;
          return axiosInstance(originalRequest);
        } catch (refreshError) {
          // 刷新 Token 失败,清除令牌并跳转到登录页
          authStore.clearTokens();
          window.location.href = '/login';
          return Promise.reject(refreshError);
        }
      }
    }

    return Promise.reject(error);
  }
);

export default axiosInstance;
  1. 实现登录和刷新 Token 的逻辑

在登录和刷新 Token 的逻辑中,更新 Pinia Store 中的令牌。

登录逻辑

// src/api/auth.ts
import axiosInstance from '@/utils/axiosInstance';
import { useAuthStore } from '@/stores/authStore';

export const login = async (email: string, password: string) => {
  const response = await axiosInstance.post('/auth/login', { email, password });
  const authStore = useAuthStore();
  authStore.setTokens(response.data.accessToken, response.data.refreshToken);
};

刷新 Token 逻辑

// src/api/auth.ts
export const refreshToken = async () => {
  const authStore = useAuthStore();
  const response = await axiosInstance.post('/auth/refresh', {
    refreshToken: authStore.refreshToken,
  });
  authStore.setTokens(response.data.accessToken, authStore.refreshToken);
};
  1. 在组件中使用

在登录组件中调用登录逻辑,并在需要认证的请求中使用 Axios 实例。

<template>
  <div>
    <input v-model="email" placeholder="Email" />
    <input v-model="password" type="password" placeholder="Password" />
    <button @click="handleLogin">Login</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { login } from '@/api/auth';

const email = ref('');
const password = ref('');

const handleLogin = async () => {
  await login(email.value, password.value);
};
</script>

获取用户数据

<template>
  <div>
    <h1>User Profile</h1>
    <p>{{ userData }}</p>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import axiosInstance from '@/utils/axiosInstance';

const userData = ref(null);

onMounted(async () => {
  const response = await axiosInstance.get('/user/profile');
  userData.value = response.data;
});
</script>

总结

通过结合 Vue 3、TypeScript、Axios 和 Pinia,可以实现无感刷新功能,具体步骤如下:

  1. 创建 Pinia Store:管理用户的认证状态和令牌。

  2. 配置 Axios 实例:添加请求拦截器和响应拦截器,自动刷新 Token。

  3. 实现登录和刷新 Token 逻辑:更新 Pinia Store 中的令牌。

  4. 在组件中使用:调用登录逻辑和认证请求。

通过无感刷新,可以提升用户体验,避免频繁重新登录。

更多vue相关插件及后台管理模板可访问vue admin reference,代码详情请访问github