解决pinia中$subscribe回调方法mutation.events无法在生产环境中使用的替代方法

发表于:2023-05-25 10:13
技术,vue
热度:155
喜欢:0

有点标题党,实际上解决的$subscribe.mutation.events中 变化的key无法的得知的问题

vue3的项目中,使用pinia做状态管理,如果想要在组件中监听state中变化,可以使用 $subscribe做监听。

store定义

typescript 复制代码
// store/app.ts
interface AppState {
  theme: string,
  [key: string]: any
}
export const useAppStore = defineStore('app', {
  state(): AppState {
    return {
      theme: 'dark'
    }
  },
  actions: {
    setTheme(theme: string) {
      this.theme = theme;
    },
    setValueByKey<T extends keyof AppState>(key: T, value: AppState[T]) {
      Object.assign(this.$state, {
        [key]: value
      })
    }
  }
})

$subscribe使用方法

typescript 复制代码
// app.vue
const appStore = useAppStore();
appStore.$subscribe((mutation, state) => {
  // import { MutationType } from 'pinia'
  mutation.type // 'direct' | 'patch object' | 'patch function'
  // 和 cartStore.$id 一样
  mutation.storeId // 'cart'
  // 只有 mutation.type === 'patch object'的情况下才可用
  mutation.payload // 传递给 cartStore.$patch() 的补丁对象。

  mutation.event

  // 每当状态发生变化时,将整个 state 持久化到本地存储。
  localStorage.setItem('cart', JSON.stringify(state))
})

bugs

在开发者模式下,mutation中会有一个events对象,控制台可以打印出store的具体变化,key,newValue,oldValue等

但是却无法再生产环境下使用

官方讨论的结果
https://github.com/vuejs/pinia/discussions/1117

所以想要监听store的具体是哪个key发生了变化的需求就无法实现了。

解决方案

我们知道,watch方法可以监听一整个对象的变化,也可以监听对象的具体属性变化,

typescript 复制代码
const data = reactive({
  a: "a",
  b: 'b'
});

watch(data, (newVal, oldVal) => { })
watch(() => data.a, (newVal, oldVal) => { })

const appStore = useAppStore();
//使用watch 监听每一个对象
const watchs = (() => {
  return Object.keys(appStore.$state).map(key => {
    return watch(() => appStore.$state[key], newVal => {
      // .... 
      bus.$emit(`on-${key}-change`, newVal)
    }, { immediate: true })
  })
});

let watchEffects = watchs();
//当state有动态添加或者删除的属性时,解除监听,并重新执行依赖收集
appStore.$subscribe((mutation, state) => {
  if (Object.keys(state).length !== watchEffects.length) {
    // 清空watch
    watchEffects.forEach(e => e());
    // 重新监听对象的每一个属性
    watchEffects = watchs()
  }
})