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

如何在Pinia Store中使用类及其Getters并解决响应式失效问题?

Pinia中类实例的响应式问题解决

问题分析

你在Pinia Store里用Character类创建角色实例,存入/读取localStorage后,类里的getter或computed属性在组件中无法触发响应式更新,核心原因有两个:

  1. 类实例的属性默认不是Vue的响应式对象,Vue无法跟踪其内部属性的变化
  2. 直接在类构造函数中给level赋值computed()时,依赖的this.general.classes不是响应式的,导致计算属性无法感知变化

修正方案

要让类实例的属性具备响应式,需要在类内部用reactive()包裹需要跟踪的对象,同时用getter来实现动态计算的level。以下是修正后的代码:

export const use_game_store = defineStore('game_store', () => {
    const characters = ref([])

    class Character {
        constructor(existing_character) {
            // 用reactive包裹general对象,确保内部属性(name、classes)响应式
            this.general = reactive({
                name: existing_character?.general.name || '',
                classes: existing_character?.general.classes || [],
                // 用getter计算总等级,自动依赖响应式的classes数组
                get level() {
                    return this.classes.reduce((total, cls) => total + cls.level, 0)
                }
            })
            // 同样用reactive包裹attributes对象
            this.attributes = reactive(existing_character?.attributes || {})
        }
    }

    // 从localStorage加载角色数据
    const loadCharacters = () => {
        const savedData = localStorage.getItem('game_characters')
        if (savedData) {
            const parsedData = JSON.parse(savedData)
            // 将保存的普通对象转为Character实例
            characters.value = parsedData.map(data => new Character(data))
        }
    }

    // 将角色数据保存到localStorage(需转为普通对象)
    const saveCharacters = () => {
        const plainCharacters = characters.value.map(char => ({
            general: {...char.general},
            attributes: {...char.attributes}
        }))
        localStorage.setItem('game_characters', JSON.stringify(plainCharacters))
    }

    return {
        characters,
        loadCharacters,
        saveCharacters
    }
})

组件中使用示例

组件里直接从Store获取characters即可,无需额外转成ref,Pinia会自动处理响应式:

<template>
  <div v-for="char in characters" :key="char.general.name">
    <h3>{{ char.general.name }}</h3>
    <p>总等级:{{ char.general.level }}</p>
    <button @click="upgradeFirstClass(char)">升级第一个职业</button>
  </div>
</template>

<script setup>
import { use_game_store } from '@/stores/game'
const gameStore = use_game_store()
const { characters, saveCharacters } = gameStore

// 加载已保存的角色
gameStore.loadCharacters()

const upgradeFirstClass = (char) => {
  if (char.general.classes.length > 0) {
    char.general.classes[0].level += 1
    // 保存更新后的角色数据
    saveCharacters()
  }
}
</script>

关键注意点

  • 响应式包裹:类内部需要响应式的对象必须用reactive()或ref()包裹,Vue才能跟踪其变化
  • localStorage存储:保存时要把响应式对象转为普通JS对象(用扩展运算符...或toRaw()),读取后再重新创建Character实例
  • 计算属性实现:用类的getter比直接用computed()更可靠,因为getter会自动依赖响应式属性,而computed()在类构造函数中容易出现this指向或依赖跟踪问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 23:12:40