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() }) }, } })
问题原因
- Composition API与Options API混合导致上下文异常:setup中获取的
userStore未正确挂载到Options API的组件实例上下文,导致this.userStore为undefined。 - 参数传递错误:组件调用
setToken时传入response.data.access(字符串类型),但setToken方法期望接收包含access和refresh字段的对象,后续会引发新的错误。 - 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
相关产品推荐
相关产品推荐

