Pinia 中调用 Getter 报错 "is not a function" 的原因及最佳实践

Pinia 中调用 Getter 报错 "is not a function" 的原因及最佳实践

_

在 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 前缀,交由后端处理
}

📝 总结

  1. Pinia 中的 getters 等价于 Vue 的 computed,使用时不要加括号

  2. 尽量避免编写只返回 state 的冗余 getter,直接通过 store.xxx 访问 state 即可,代码更简洁。

  3. 在请求拦截器中组装 JWT 鉴权头时,推荐前后端约定好格式(例如在后端 Sa-Token 中配置 token-prefix: Bearer),前端保持纯净的 Token 传递。

优雅处理前端回显:在 VO 中使用 private static 内部类的巧思 2026-08-31
Spring Boot 踩坑记录:@Value 无法注入 YAML 数组及完美解决方案 2026-09-04

评论区