◆
Vue Pinia 使用 $patch 整体更新 state 数据
一次要改 store 里好几个字段时,逐个 store.a = 1; store.b = 2 地赋值有两个问题:写起来啰嗦,而且每次赋值都是一次独立的响应式变更,在 devtools 里会记成多条记录,订阅者也会被触发多次。$patch 就是为这个场景准备的——传一个对象进去,Pinia 合并进 state 并只产生一次变更。
这里有个容易踩的点:$patch 传对象时是浅层合并。顶层字段按传入的键覆盖,没传的键保持不变;但嵌套对象是整体替换,不会往里递归合并。下面的例子专门验证了这一点:第二次只传 attr: { name1: 'www' },原来的 name2、name3 会一并消失,而不是只改 name1。
另外顺带演示 $state(直接读整个 state)和 $reset(一键恢复到 state() 的初始值)。
store 定义如下
标准的 option 式 store。重点在 updateStates 这个 action:参数类型用 Partial<AppState> 标注,表示允许只传部分字段,正好匹配 $patch 的语义,TS 层面也就不会因为字段缺失报错了。getAllState 返回 this.$state,拿到的是整个 state 的响应式代理。
import { defineStore } from 'pinia';
import pinia from '@/store';
interface AppState {
name: string;
value: string;
status: boolean;
cache: Record<string, any>;
attr: Record<string, any>;
}
export const useAppStore = defineStore({
id: 'app',
state: (): AppState => ({
name: 'test',
value: '',
status: false,
cache: {},
attr: {},
}),
actions: {
updateStates(states: Partial<AppState>) {
this.$patch(states);
},
getAllState() {
return this.$state;
},
},
});
export function useAppStoreWithOut() {
return useAppStore(pinia);
}
组件侧用三个定时器分阶段触发,模板里同时渲染 $state 和 getAllState() 的结果,可以直接观察每一步的变化:
- 1.5s 时传入完整字段,
attr里有name1/name2/name3 - 3.5s 时只传
name和attr.name1。这时value、status因为没传所以保持不变(浅层合并的「合并」部分),但attr被整体替换成了只含name1的新对象,name2、name3丢失(浅层合并的「浅」部分) - 5.5s 时
$reset()把所有字段拉回state()里定义的初值
如果确实需要深层合并只改嵌套对象里的某个键,$patch 得改用函数式写法 $patch(state => { state.attr.name1 = 'www' }),在回调里精确改动,这样不会替换整个 attr。
<script lang="ts" setup>
const appStore = useAppStore();
setTimeout(() => {
appStore.updateStates({
name: '测试',
value: '更新',
status: true,
attr: {
name1: 'w',
name2: 'f',
name3: 'd',
},
});
}, 1500);
setTimeout(() => {
appStore.updateStates({
name: '测试222',
attr: {
name1: 'www',
},
});
}, 3500);
setTimeout(() => {
appStore.$reset();
}, 5500);
</script>
<template>
<div>
{{ appStore.$state }}
</div>
<br />
<br />
<div>
{{ appStore.getAllState() }}
</div>
</template>

还没有留言,来发表第一条吧~