Vue 不会使用动态组件从 Vuex 更新 v-for 中的项目

Posted

技术标签:

【中文标题】Vue 不会使用动态组件从 Vuex 更新 v-for 中的项目【英文标题】:Vue does not update items in v-for from Vuex with dynamic component 【发布时间】:2019-12-23 00:24:15 【问题描述】:

我们有一个tab body 的动态组件,定义为

<component :is="currentTab.itemType" :itemId="currentTab.itemId"></component>

模板有一个跨度,它反映了itemId - 每次标签主机组件中的currentTab 更改时,它都会更改。

tab.itemType 的每个组件都有 Vuex 模块,属于它特定的类型。

例如,有描述状态的存储模块product


  products:  [itemId: string]: IProduct 

当组件创建或itemId改变时,它会尝试运行加载动作并将加载的产品置于vuex状态的products

所以,有 Vue 计算属性,看起来像

@State(productNamespace)
state: IProductState;

get currentProduct() 

  return this.state.products[this.itemId];

甚至

@Getter(GetterNames.GET_PRODUCT_BY_ID, bindingOptions)
getProductById: (itemId: string) => IProduct;

get currentProduct() 

  return this.getProductById(this.itemId);

每个产品都有attributes 列表,由v-for:key 迭代。

<v-list :key="itemId"><!-- itemId has no effect there -->
  <v-list-item v-for="attribute in currentProduct.attributes" :key="attribute.id">
    ...
  </v-list-item>
</v-list>

问题是: 当我们更改itemId 时,属性列表会显示上次添加产品的所有属性,并且在使用另一个itemId 但相同的itemType 切换到以前的“选项卡”时不会刷新它。

我尝试将父级div:key 设置为itemId,但没有任何效果。 当我将:key 设置为&lt;component&gt; 时,vuex 状态会被破坏。

Vue 版本是 2.6.10

更新:

它也不适用于产品的简单属性:

 currentProduct.name 

总结:

itemId 属性。计算属性取决于它。因此,当 itemId 属性更改而 Vuex 集合未更改时,计算属性不会反映更改。

确认:

计算属性仅在 state.products 集合更改时更新。我通过为每个选项卡切换运行 createProduct 操作来模拟这一点。 vuex 状态下的集合接受未观察的产品存根并反映对合法currentProduct 的更改,并带有给定的itemId

更新 2:带有观察者的组件。还是没办法……

@Component
export default class Product extends Vue 

  @Prop( type: Object, required: true )
  readonly tabItem: ITabItem;

  @State(productNamespace)
  state: IProductState;

  itemId: string;

  created() 

    //...
    this.initCurrentProduct();
  

  // No changes until state.products was changed.   
  get currentProduct(): IProduct |  

    if (!this.state) return ;     
    return this.state.products[this.itemId];
  

  @Watch('tabItem')
  onTabItemChanged()
  
    DEBUG && console.log('Tab changed: keep moving!');
    this.initCurrentProduct();
  

  private async initCurrentProduct() 

    const  isNew, itemId  = this.tabItem;

    if (itemId === this.itemId)
      return;

    DEBUG && console.log('ItemId changed.');
    this.itemId = itemId;

    // ...
  

  // ...


【问题讨论】:

您不应该访问 this.$store.state... 而不是 this.$state...? 对不起,我的错 - 我使用'vuex-class',所以有不正确的简化。等一下,我纠正一下。 我记得 Vuex 推荐 getter 用于计算属性,而不是直接访问 Store。您是否尝试过使用吸气剂? 是的,原来是从功能性吸气剂中获取的。似乎只有添加的最后一项反映了它的属性,但切换回来对更改 itemId 的计算属性没有影响。所以我认为有products 集合更改事件引发。 我不确定我们是否在同一页面上。你说有一个吸气剂,但在显示的代码中你只是直接访问商店而没有任何吸气剂。计算的属性也是get currentProduct() - 为什么是空格?它在计算部分吗? 【参考方案1】:

在你的 vuex 突变中

let items = [...state.items]; // create a new copy

// mutate it 
items.map(item => item.selected = true);

// return the new copy
state.items = items;

【讨论】:

【参考方案2】:

好的,所以您传递给动态组件的属性是 currentTab.itemId,这意味着 itemId 实际上是 currentTab 对象中的一个元素,而不是根 Vue 数据对象?

Vue 默认不跟踪嵌套对象,它只会在整个对象发生更改时触发重绘(例如,如果您执行currentTab = ... 之类的操作)。您可以:

    currentTab 上使用带有deep: true 属性的观察程序:https://vuejs.org/v2/api/#watch,然后在调用时使用this.$forceUpdate 触发重绘。

    itemId 移动到数据的根目录,然后从那里更新它

【讨论】:

用观察者和 itemId 作为数据的组件示例更新了问题。仍然没有效果......但是等等! itemId 的初始值是未定义的,所以它不能是响应式的!更改为默认itemId: string = '';,现在可以使用了!

以上是关于Vue 不会使用动态组件从 Vuex 更新 v-for 中的项目的主要内容,如果未能解决你的问题,请参考以下文章

如何从 vuex-store 更新 vue 子组件

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

从持久存储重新水化后,Vuex getter 不会更新

VueX:为啥 vuex 存储数据不更新组件数据属性?

如何在 Vuex 商店的 Vue.js 组件中设置动态样式

Vuex持久化 &vue组件设置背景色