在 Vue 3 项目中从 Vuex 迁移到 Pinia 或者初次使用 Pinia 时,很多开发者(包括我自己)都会习惯性地写一堆
getters,然后在组件或拦截器中调用。但这往往会导致一个经典的报错。今天在配置 Axios 请求拦截器时,就踩到了这个小坑,特此记录,以备忘却。
🐛 问题复现
在定义权限和用户信息的 Store 时,我是这样写 getters 的:
import { defineStore } from "pinia";
export const userStore = defineStore("userStore", {
state: () => {
return {
token: "eyJhbGciOiJIUzI1Ni... (模拟token)",
};
},
getters: {
// 习惯性地写了一个 getter
getToken(state) {
return state.token;
}
}
});
然后,在 Axios 请求拦截器中,我试图获取这个 Token 并塞进请求头里:
// 请求拦截器
request.interceptors.request.use((config) => {
// 报错发生在这里!
const token = userStore().getToken();
if (token) {
config.headers.Authorization = token;
}
return config;
});
运行代码后,控制台无情地抛出了一个错误: TypeError: userStore(...).getToken is not a function
🔍 原因分析
为什么明明在 getters 里定义了一个 getToken(state) 函数,调用时却说它不是一个 function 呢?
原因在于 Pinia 对 Getters 的底层处理机制: 在 Pinia 中,getters 并不是普通的方法(Methods),而是会在底层被转换为 Vue 的 计算属性(Computed Properties)。
当你调用 userStore() 拿到 Store 实例后,getToken 已经变成了一个具有缓存特性的属性值,而不是一个可执行的函数。
userStore().getToken👉 正确:拿到的是计算属性的值(字符串)。userStore().getToken()👉 错误:试图把字符串当成函数去执行,JavaScript 引擎自然会报is not a function。
💡 解决方案
最直接的解决办法,就是去掉调用时的括号,把它当成属性来访问:
// ✅ 正确写法:去掉括号
const token = userStore().getToken;
🚀 进阶:Pinia 的最佳实践
虽然去掉括号能解决问题,但在查阅 Pinia 官方文档后,我发现了更优雅的“最佳实践”:完全没必要写这种“只是为了返回 state”的 Getters!
在以前的 Vuex 时代,我们习惯用 getters 来获取 state。但 Pinia 架构做出了精简,state 本身就是直接对外暴露且保持响应式的。
所以,最优雅的代码改造方式是——直接删掉这些冗余的 Getters,直接读取 State!
终极优化版 Store 代码:
import { defineStore } from "pinia";
export const userStore = defineStore("userStore", {
state: () => {
return {
token: "",
user: {},
permissions: [],
menus: [],
};
},
// 🗑️ 直接删掉 getters 这一块!
actions: {
setToken(token) {
this.token = token;
},
// ... 其他 actions
}
});
在拦截器或组件中的使用方式:
极其清爽,只需点出属性名即可:
// 直接访问 state 中的 token
const token = userStore().token;
if (token) {
config.headers.Authorization = token; // 无需再加 Bearer 前缀,交由后端处理
}
📝 总结
Pinia 中的
getters等价于 Vue 的computed,使用时不要加括号。尽量避免编写只返回 state 的冗余 getter,直接通过
store.xxx访问 state 即可,代码更简洁。在请求拦截器中组装 JWT 鉴权头时,推荐前后端约定好格式(例如在后端 Sa-Token 中配置
token-prefix: Bearer),前端保持纯净的 Token 传递。