You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3使用Pinia调用方法报错:Cannot read properties of undefined (reading 'setToken')

Vue3中使用Pinia调用setToken时出现TypeError问题排查与解决

问题现象

调用Pinia的setToken方法时抛出错误:

TypeError: Cannot read properties of undefined (reading 'setToken')

相关代码

组件代码

<script>
import axios from 'axios'
import { useUserStore } from '@/stores/user'

export default {
    setup() {
        const userStore = useUserStore()
        return {
            userStore
        }
    },
    data() {
        return {
            form: {
                email: '',
                password: '',
            },
            errors: []
        }
    },
    methods: {
        async submitForm() {
            this.errors = []
            if (this.form.email === '') {
                this.errors.push('邮箱不能为空')
            }
            if (this.form.password === '') {
                this.errors.push('密码不能为空')
            }
            if (this.errors.length === 0) {
                await axios
                    .post('/api/login/', this.form)
                    .then(response => {
                        this.userStore.setToken(response.data.access)
                        console.log(response.data.access)
                        axios.defaults.headers.common["Authorization"] = "Bearer " + response.data.access;
                    })
                    .catch(error => {
                        console.log('error', error)
                    })
                
                await axios
                    .get('/api/auth/')
                    .then(response => {
                        this.userStore.setUserInfo(response.data)
                        this.$router.push('/dashboard-banking')
                    })
                    .catch(error => {
                        console.log('error', error)
                    })
            }
        }
    }
}
</script>

Pinia Store代码(user.js)

import { defineStore } from 'pinia'
import axios from 'axios'

export const useUserStore = defineStore("user",{
    state: () => ({
        user: {
            isAuthenticated: false,
            id: null,
            name: null,
            email: null,
            organization: null,
            role: [],
            access: null,
            refresh: null,
        }
    }),
    actions: {
        initStore() {
            console.log('initStore', localStorage.getItem('user.access'))
            if (localStorage.getItem('user.access')) {
                console.log('用户已登录')
                this.user.access = localStorage.getItem('user.access')
                this.user.refresh = localStorage.getItem('user.refresh')
                this.user.id = localStorage.getItem('user.id')
                this.user.name = localStorage.getItem('user.name')
                this.user.email = localStorage.getItem('user.email')
                this.user.isAuthenticated = true
                this.refreshToken()
                console.log('初始化用户信息:', this.user)
            }
        },
        setToken(data) {
            console.log('setToken', data)
            this.user.access = data.access
            this.user.refresh = data.refresh
            this.user.isAuthenticated = true
            localStorage.setItem('user.access', data.access)
            localStorage.setItem('user.refresh', data.refresh)
            console.log('存储的access令牌: ', localStorage.getItem('user.access'))
        },
        removeToken() {
            console.log('removeToken')
            this.user.refresh = null
            this.user.access = null
            this.user.isAuthenticated = false
            this.user.id = false
            this.user.name = false
            this.user.email = false
            localStorage.setItem('user.access', '')
            localStorage.setItem('user.refresh', '')
            localStorage.setItem('user.id', '')
            localStorage.setItem('user.name', '')
            localStorage.setItem('user.email', '')
        },
        setUserInfo(user) {
            console.log('setUserInfo', user)
            this.user.id = user.id
            this.user.name = user.name
            this.user.email = user.email
            localStorage.setItem('user.id', this.user.id)
            localStorage.setItem('user.name', this.user.name)
            localStorage.setItem('user.email', this.user.email)
            console.log('用户信息:', this.user)
        },
        testCall(){
            console.log('测试调用')
        },
        refreshToken() {
            axios.post('/api/refresh/', {
                refresh: this.user.refresh
            })
                .then((response) => {
                    this.user.access = response.data.access
                    localStorage.setItem('user.access', response.data.access)
                    axios.defaults.headers.common["Authorization"] = "Bearer " + response.data.access
                })
                .catch((error)=>{
                    console.log(error)
                    this.removeToken()
                })
        },
    }
})

问题原因

  1. Composition API与Options API混合导致上下文异常:setup中获取的userStore未正确挂载到Options API的组件实例上下文,导致this.userStore为undefined。
  2. 参数传递错误:组件调用setToken时传入response.data.access(字符串类型),但setToken方法期望接收包含access和refresh字段的对象,后续会引发新的错误。
  3. Pinia初始化缺失(潜在问题):若项目入口文件未正确初始化Pinia,会导致useUserStore无法获取有效store实例。

解决步骤

步骤1:确保Pinia正确初始化

在项目入口文件(main.js)中添加Pinia初始化代码:

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'

const app = createApp(App)
// 初始化并挂载Pinia
app.use(createPinia())
app.use(router)
app.mount('#app')

步骤2:统一使用Composition API重构组件(推荐)

避免混合两种API写法,改用<script setup>语法消除this指向问题:

<script setup>
import axios from 'axios'
import { useUserStore } from '@/stores/user'
import { useRouter } from 'vue-router'

// 获取store和路由实例
const userStore = useUserStore()
const router = useRouter()

// 定义表单和错误信息
const form = {
    email: '',
    password: ''
}
const errors = []

// 重构提交方法
const submitForm = async () => {
    errors.length = 0
    if (!form.email) errors.push('邮箱不能为空')
    if (!form.password) errors.push('密码不能为空')

    if (errors.length === 0) {
        try {
            // 登录请求
            const loginRes = await axios.post('/api/login/', form)
            // 传入完整响应数据(包含access和refresh)
            userStore.setToken(loginRes.data)
            // 设置请求头
            axios.defaults.headers.common["Authorization"] = `Bearer ${loginRes.data.access}`

            // 获取用户信息
            const userRes = await axios.get('/api/auth/')
            userStore.setUserInfo(userRes.data)
            // 跳转到仪表盘
            router.push('/dashboard-banking')
        } catch (error) {
            console.log('请求失败:', error)
        }
    }
}
</script>

步骤3:若坚持使用Options API,使用Pinia辅助函数

通过mapStores辅助函数将store映射到组件计算属性:

<script>
import axios from 'axios'
import { useUserStore } from '@/stores/user'
import { mapStores } from 'pinia'

export default {
    computed: {
        // 将userStore映射到组件实例
        ...mapStores(useUserStore)
    },
    data() {
        return {
            form: {
                email: '',
                password: '',
            },
            errors: []
        }
    },
    methods: {
        async submitForm() {
            this.errors = []
            if (this.form.email === '') {
                this.errors.push('邮箱不能为空')
            }
            if (this.form.password === '') {
                this.errors.push('密码不能为空')
            }
            if (this.errors.length === 0) {
                try {
                    const loginRes = await axios.post('/api/login/', this.form)
                    // 修正参数,传入完整响应数据
                    this.userStore.setToken(loginRes.data)
                    axios.defaults.headers.common["Authorization"] = `Bearer ${loginRes.data.access}`

                    const userRes = await axios.get('/api/auth/')
                    this.userStore.setUserInfo(userRes.data)
                    this.$router.push('/dashboard-banking')
                } catch (error) {
                    console.log('请求失败:', error)
                }
            }
        }
    }
}
</script>

步骤4:修正setToken参数传递

确保调用setToken时传入包含access和refresh的完整响应对象,而非单独的access字符串,避免后续出现Cannot read properties of string (reading 'access')错误。

内容的提问来源于stack exchange,提问作者ApDorji

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 02:08:13