Vue3+Pinia Getter无法响应式更新Vue响应式变量问题
问题:Tauri+Vue+Pinia状态更新后子组件标签禁用状态未同步
我正在开发一个带状态控制的Tauri应用,已排除Tauri相关问题。应用通过异步进程检查系统状态并更新Pinia中的appStatus,供子组件使用。最初appStatus是对象,后来把status属性单独拆分出来,问题依然存在:子组件的标签禁用状态不会随着appStatus的更新而变化。
Pinia Store 代码(general.js)
import { defineStore } from 'pinia'; export const useGeneralStore = defineStore('general', { state: () => ({ appStatus: 'loading', appStatusData: { status: 'loading', license_number: '', days_left: 0, valid_until: '', }, showOverlay: false, }), getters: { getShowOverlay: (state) => state.showOverlay, disabledWhen: (state) => { return (conditions) => { if (conditions.includes(state.appStatus)) return true; return false; }; }, }, actions: { setAppStatus(value) { this.appStatus = value; }, setShowOverlay(value) { this.showOverlay = value; }, }, });
启动状态检查代码(App.vue)
.... <script setup> import { onMounted } from 'vue'; import { Command } from '@tauri-apps/api/shell'; import { storeToRefs } from 'pinia'; import { useGeneralStore } from './store/general'; import { ref } from 'vue'; const store = useGeneralStore(); const { setDialog } = store; const { showOverlay, appStatus, appStatusData, fernetKey } = storeToRefs(store); onMounted(async () => { showOverlay.value = true; const command = Command.sidecar('binaries/app', ['status', fernetKey.value]); let out = await command.execute(); if (out.code !== 0) { setDialog(out.stderr); return; } out = JSON.parse(out.stdout); appStatus.value = out.status; appStatusData.value = out; showOverlay.value = false; }); </script> ...
子组件标签页代码(ActionTabs.vue)
<template> <div class="actions_side w-12 font-montserrat-regular relative"> <TabView :active-index="enabledIndex"> <TabPanel :disabled="tab.disabled" v-for="tab in actionTabs" :key="tab.title" :header="tab.title" > <component :is="tab.component" /> </TabPanel> </TabView> </div> </template> <script setup> import { ref, computed, reactive } from 'vue'; import Activation from '../components/Activation.vue'; import Deactivation from '../components/Deactivation.vue'; import Install from '../components/Install.vue'; import { useGeneralStore } from '../store/general'; import { storeToRefs } from 'pinia'; const store = useGeneralStore() const { appStatus, disabledWhen } = storeToRefs(store) const actionTabs = reactive([ { title: 'Activate', component: Activation, disabled: disabledWhen.value(['active', 'notFound', 'loading']), }, { title: 'Deactivate', component: Deactivation, disabled: disabledWhen.value(['deactive', 'notFound', 'loading']), }, { title: 'Install', component: Install, disabled: true, }, { title: 'Update', component: Activation, disabled: true, }, ]); const enabledIndex = computed(() => { for (let i = 0; i < actionTabs.length; i++) { if (!actionTabs[i].disabled) return i; } }); </script>
已尝试的解决方法
- 将
reactive改为ref - 将Getter替换为本地判断(注释代码仍保留)
- 将原始值从对象中分离
- 通过按钮修改状态值并确认状态已正确变更
解决方案
核心问题是:actionTabs中的disabled属性是组件初始化时一次性计算的,没有响应式追踪appStatus的变化。需要将标签的禁用状态改为响应式计算,确保appStatus更新时同步更新禁用状态。
修改后的ActionTabs.vue代码:
<template> <div class="actions_side w-12 font-montserrat-regular relative"> <TabView :active-index="enabledIndex"> <TabPanel :disabled="tab.disabled" v-for="tab in computedActionTabs" :key="tab.title" :header="tab.title" > <component :is="tab.component" /> </TabPanel> </TabView> </div> </template> <script setup> import { ref, computed } from 'vue'; import Activation from '../components/Activation.vue'; import Deactivation from '../components/Deactivation.vue'; import Install from '../components/Install.vue'; import { useGeneralStore } from '../store/general'; import { storeToRefs } from 'pinia'; const store = useGeneralStore() const { appStatus, disabledWhen } = storeToRefs(store) // 基础标签配置,仅存固定属性 const baseActionTabs = [ { title: 'Activate', component: Activation, disableConditions: ['active', 'notFound', 'loading'], }, { title: 'Deactivate', component: Deactivation, disableConditions: ['deactive', 'notFound', 'loading'], }, { title: 'Install', component: Install, disableConditions: [], }, { title: 'Update', component: Activation, disableConditions: [], }, ]; // 通过computed生成响应式标签列表,实时计算禁用状态 const computedActionTabs = computed(() => { return baseActionTabs.map(tab => { let disabled = false; if (tab.disableConditions.length > 0) { disabled = disabledWhen.value(tab.disableConditions); } else { // 对应Install和Update的固定禁用 disabled = true; } return { ...tab, disabled, }; }); }); const enabledIndex = computed(() => { for (let i = 0; i < computedActionTabs.value.length; i++) { if (!computedActionTabs.value[i].disabled) return i; } }); </script>
改动说明
- 将原本的
reactive数组拆分为基础配置和computed响应式数组,每次appStatus变化时,computedActionTabs会重新计算每个标签的禁用状态。 - 避免了初始化时一次性计算
disabled的问题,确保状态更新时禁用状态同步变化。
内容的提问来源于stack exchange,提问作者Safa Emre YILDIRIM
相关产品推荐
相关产品推荐

