Skip to content

title: 退出登录后跳转与请求是否会发出?(Vue Router 4 实战) date: 2025-09-05 tags: [Vue, Vue Router, Pinia, Axios, 登录, 退出]


TL;DR(先给结论)

  • router.push('/user') 不会去加载 @/views/(public)/layout.vue/user 是独立的公开路由,全局前置守卫会跑,再决定是否进入目标路由,受保护布局组件不会被解析/执行。
  • 真正会“误发请求”的常见来源是:① 旧页中已发起但未取消的请求;② <keep-alive> 相关的激活/停用副作用;③ 你在 HTTP 层没有对“无 Token 的鉴权请求”做硬性拦截
  • 给你三道保险:彻底清 Token + 取消未决请求 + 请求层强拦(可选再加根路由 beforeEnter)。

最小修改(仅给需要改的代码片段)

说明:按你的偏好,只给需修改片段;复杂逻辑在上一行加注释。

1) auth Store:退出时同时清理本地存储 Token、取消所有请求、清默认头

文件: useAuthStore 所在文件

ts
// 新增导入
import storage from "store";
import { STORAGE_KEYS } from "@/constants";
const { ACCESS_TOKEN } = STORAGE_KEYS;
ts
// 新增:统一清空本地 Token(Pinia + 本地存储)
function clearToken() {
  // 复杂逻辑:双通道清理,避免路由守卫误判已登录
  token.value = null;
  refresh.value = null;
  storage.remove(ACCESS_TOKEN);
}
ts
// 调整 logout 函数(仅贴需要增加的片段)
async function logout() {
  try {
    await http.post("/api/pub/loginOut").catch(() => {});
    // 复杂逻辑:优雅下线——取消所有还在飞行中的请求,防止“未登录也发请求”
    http.cancelAll?.();
    // 复杂逻辑:清掉默认鉴权头,防止后续请求夹带旧 Token
    http.setAuthToken?.(null);

    clearToken();
    resetAllStores();
  } catch (error) {
    console.error(error);
    throw error;
  }
}

你现在的调用 await authStore.logout(); router.push('/user') 没问题;若不想留下历史记录,可换成:

ts
// 可选:避免回退到已登录页
await router.replace({ name: "user" });

2) http 工具:加“全局取消池 + 无 Token 时硬拦需要鉴权的请求”

文件: 你的 @/utils/http(或创建一个轻量封装)

ts
// 新增:全局取消池
const abortPool = new Set<AbortController>();

export function cancelAll() {
  // 复杂逻辑:退出登录时集中取消所有未决请求
  abortPool.forEach((c) => c.abort());
  abortPool.clear();
}

export function setAuthToken(token?: string | null) {
  // 复杂逻辑:动态设置/清除默认鉴权头
  if (token) {
    axios.defaults.headers.common.Authorization = `Bearer ${token}`;
  } else {
    delete axios.defaults.headers.common.Authorization;
  }
}
ts
// 在请求拦截里增加以下片段(保留你已有逻辑)
http.interceptors.request.use((config) => {
  // 复杂逻辑:为每个请求绑定 AbortController,纳入全局取消池
  const ac = new AbortController();
  (config as any).signal = ac.signal;
  abortPool.add(ac);

  // 复杂逻辑:对标记了 { auth: true } 的请求做硬性校验,无 Token 直接终止
  const needAuth = (config as any).auth === true;
  const token = storage.get(ACCESS_TOKEN);
  if (needAuth && !token) {
    ac.abort();
    // 用标准错误中断;也可自定义错误码
    throw new axios.Cancel("UNAUTHENTICATED");
  }
  return config;
});
ts
// 在响应/错误拦截里释放对应的 controller(保持池干净)
const detach = (cfg?: any) => {
  if (!cfg?.signal) return;
  for (const ac of abortPool) {
    if (cfg.signal === ac.signal) {
      abortPool.delete(ac);
      break;
    }
  }
};
http.interceptors.response.use(
  (resp) => {
    detach(resp.config);
    return resp;
  },
  (err) => {
    detach(err?.config);
    return Promise.reject(err);
  }
);

使用方式:凡是需要登录才能访问的接口,在调用时带上 { auth: true }

ts
// 复杂逻辑:打上鉴权标记,无 Token 时在请求层被硬拦
http.get("/api/secure/profile", { auth: true });

3) 根布局路由(可选加固):未登录禁止进入,阻止懒加载 layout.vue

这是“冗余但稳”的第四道锁:即使有人误配了守卫,根布局也进不去。

文件: 路由表所在文件

ts
// 新增导入(如果本文件里还没有)
import storage from "store";
import { STORAGE_KEYS } from "@/constants";
import { LOGIN_ROUTE } from "./constants";
const { ACCESS_TOKEN } = STORAGE_KEYS;
ts
// 在 path: '/' 那个根路由对象上新增 beforeEnter(只贴新增的属性)
beforeEnter: (to, from, next) => {
  // 复杂逻辑:未登录直接拒绝进入受保护根布局,避免解析/执行 layout.vue
  const token = storage.get(ACCESS_TOKEN)
  if (!token) return next({ name: LOGIN_ROUTE.name, replace: true })
  next()
},

为什么你会怀疑 layout.vue 被加载?

常见场景有三种(任选对号入座):

  1. 旧页未决请求:你还在“受保护页面”时就发出了请求,push('/user') 后这些请求仍会完成,除非你主动取消(上面第 2 步已解决)。
  2. <keep-alive> 激活/停用副作用:离开/进入时有数据拉取,需要在未登录时短路。
  3. 预加载 Chunk:如果手动开启过 webpackPrefetch 等,会看到静态资源拉取,但这不等同于“业务请求”发出。

排查清单(按优先级)

  1. 退出后 localStorage/store 里是否还残留 ACCESS_TOKEN?(第 1 步已处理)
  2. 所有需要登录的接口是否统一加了 { auth: true } 并被请求拦截器“硬拦”?(第 2 步)
  3. 页面里有没有在 onMounted 直接拉数据但无 Token 校验的逻辑?(在组件中加早返回)
  4. 是否使用了 <keep-alive> 并在 activated/deactivated 发请求?(同样做登录短路)
  5. 仍担心懒加载?给根布局加 beforeEnter(第 3 步“加固锁”)。

你最关心的那句直白话

  • 不会router.push('/user') 不会去跑 @/views/(public)/layout.vue 里的业务请求。
  • :如果旧页的请求已经发出且你没有取消,它们会继续完成。上面给的三段修改能把这类“该停的请求”全部截住。

本站总访问