◆

Vue Pinia 使用 $patch 整体更新 state 数据

2023-10-08 10:590

一次要改 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. 1.5s 时传入完整字段,attr 里有 name1/name2/name3
  2. 3.5s 时只传 name 和 attr.name1。这时 value、status 因为没传所以保持不变(浅层合并的「合并」部分),但 attr 被整体替换成了只含 name1 的新对象,name2、name3 丢失(浅层合并的「浅」部分)
  3. 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>
💬留言板0

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

PAGE LOAD / LIVEVue Pinia 使用 $patch 整体更新 state 数据加载中