使用 Lodash.remove 通过 Vuex 突变更改状态不会触发我的 Vue 组件中的反应式重新渲染

Posted

技术标签:

【中文标题】使用 Lodash.remove 通过 Vuex 突变更改状态不会触发我的 Vue 组件中的反应式重新渲染【英文标题】:Changed state through Vuex mutation using Lodash.remove does not trigger a reactive re-render in my Vue component 【发布时间】:2020-12-06 07:12:23 【问题描述】:

我有一个组件从父组件接收Villager 数组到它的prop。父组件从this.$store.state 拉取该数组。

return this.$store.state.villages.find(value => value.id === 0).villagers

我之后执行this.$store.mutation 提交以更改给定的数组。正如您在下面的屏幕截图中看到的那样,突变确实有效。但是我所在的组件没有重新渲染,并且仍然显示 3 而不是 2 项。

反应链似乎在某处断裂,但我无法确定恶魔。我认为props 值是一种反应机制。正如您在屏幕截图中看到的那样,它的值已更改,但 DOM 并未相应更新。

代码摘录

我试图提取问题的相关部分

store/index.ts

[...]
export default new Vuex.Store(
  state: 
    villages: [
      
        id: 0,
        name: 'Peniuli',
        foundingDate: 20,
        villagers: [
          
            id: '33b07765-0ec6-4600-aeb1-43187c362c5a1',
            name: 'Baltasar',
            bloodline: 'Gent',
            occupation: 'Farmer'
          ,
[...]
   mutations: 
       disbandVillager (state, payload) 
         const villagerId = payload.id
         const village = state.villages.find(value => value.id === 0)
         console.debug('disbanding villager:', villagerId)
         if (!_.isNil(village)) 
           console.debug('bfore:', village.villagers)
           _.remove(village.villagers, function (n) 
             return n.id === villagerId
           )
           console.debug('after:', village.villagers)
         
       
     ,
[...]

Village.vue

<template>
    <Villagers :villagers="playerVillage.villagers"></Villagers>
</template>
[...]
  computed: 
    playerVillage: function () 
      return this.$store.state.villages.find(value => value.id === 0)
    
  
[...]

Villagers.vue

<template>
  <v-container>
    <v-card v-for="villager in villagers" :key="villager.id">
      <v-row>
        <v-col>
          <v-card-title>Name: villager.name</v-card-title>
        </v-col>
        <v-col>
          <v-card-title>Bloodline: villager.bloodline</v-card-title>
        </v-col>
        <v-col>
          <v-card-title>Occupation: villager.occupation</v-card-title>
        </v-col>
        <v-col v-if="managable">
          <DisbandButton :villager="villager"></DisbandButton>
        </v-col>
      </v-row>
    </v-card>
  </v-container>
</template>

<script>
import DisbandButton from '@/components/village/DisbandButton'

export default 
  name: 'Villagers',
  components:  DisbandButton ,
  props: [
    'villagers',
    'managable'
  ]

</script>

DisbandButton.vue

<template>
  <v-btn @click="disbandVillager" color="red">Disband</v-btn>
</template>

<script>
export default 
  name: 'DisbandButton',
  props: ['villager'],
  methods: 
    disbandVillager: function () 
      this.$store.commit('disbandVillager',  id: this.villager.id )
    
  

</script>

【问题讨论】:

【参考方案1】:

假设这是 lodash,那么我认为问题在于您使用的是 _.remove

在内部,_.remove 的实现方式不会触发 Vue 的反应系统。具体在这里:

https://github.com/lodash/lodash/blob/ded9bc66583ed0b4e3b7dc906206d40757b4a90a/lodash.js#L3859

这是使用默认的 Array splice 方法,而不是 Vue 提供的自定义覆盖。

这样的事情应该可以工作:

village.villagers = village.villagers.filter(function (n) 
  return n.id !== villagerId
)

【讨论】:

啊 dangit,你打败了我。花了太长时间挖掘 Lodash 源代码

以上是关于使用 Lodash.remove 通过 Vuex 突变更改状态不会触发我的 Vue 组件中的反应式重新渲染的主要内容,如果未能解决你的问题,请参考以下文章

Vuex 拾遗

vuex初探

vuex的使用和理解

Vuex的使用及组件通信方式

vuex总结

Vuex的工作流程,以及它的作用,使用场景