【问题标题】:Vue component not immediately updating after associated data changes关联数据更改后,Vue 组件不会立即更新
【发布时间】:2020-03-25 04:38:53
【问题描述】:

我还是 Vue 的新手,并且在连接数据更新后让组件更新其内容的问题。组件将在重新渲染后立即显示更改的内容。

组件

Vue.component('report-summary', {
  props: ['translation', 'store'],
  watch: {
    'store.state.currentModel.Definition': {
      handler: function(change) {
        console.log('Change detected', change);
      },
      deep:true
    }
  },
  template: '<div>
    '<div class="alert alert-secondary" role="alert">' +
    '<h5 class="border-bottom border-dark"> {{ translation.currently_defined }}</h5>' +
    '<div>{{ store.state.currentModel.Definition.length }} {{translation.elements }}</div>' +
    '</div>' +
    '</div>'
});

store 在页面的 HTML 中作为属性传入:

<report-summary :translation="t" :store="store"></report-summary>

store 本身就是一个Vuex.Store

let store = new Vuex.Store({
  state: {
    t: undefined,
    currentModel: undefined
  },
  mutations: {
    storeNewModel(state) {
      let model = CloneFactory.sealedClone(
        window.Project.Model.ReportTemplateModel);
      model.Header = CloneFactory.sealedClone(
        window.Project.Model.ReportTemplateHeaderModel);
      state.currentModel = model;
    },
    storeNewModelDefinition(state, definition) {
     state.currentModel.Definition.push(definition);
    }
  }
});

currentModel 元素通过在模型的Definition 属性中存储任何数据之前调用store.commit('storeNewModel'); 进行初始化。

通过在循环中使用store.commit('storeNewModelDefinition') 来更新定义的内容:

for (let c in this.$data.currentDefinition.charts) {
  store.commit('storeNewModelDefinition', c);
}

store.commit('storeNewModelDefinition', c); 被调用时,商店会按预期更新:

但组件不会对更改的数据做出反应:

然后,当我离开时(通过更改隐藏嵌入式组件的视图)并再次导航到内容已更新的视图:

在控制台窗口中,我看到观察者在任何时候都没有被触发:

我在这里缺少什么?非常感谢您提前睁开眼睛。

使用 EventBus

watch 切换到事件总线侦听器也没有按计划工作。

这是EventBus的初始化:

window.eventBus= new Vue(); 

我将此添加到我的组件中:

  created: function() {
    window.eventBus.$on('report-summary-update', function() {
      this.$nextTick(function(){ this.$forceUpdate(); });
    });
  }

并在从列表中添加或删除元素后发出事件:

window.eventBus.$emit('report-summary-update');

在查看this.$forceUpdate(); 的调试器中设置断点时,我看到它也被调用了,但 UI 仍然会显示旧内容而没有任何更改。我现在真的迷路了。


附注我在Vue forum 上交叉发布了这个,但由于社区很小,希望在这里收到一些反馈。

【问题讨论】:

  • 只是出于好奇,您有什么理由不使用vue-cli 这个项目?
  • 您是否有理由将 Vuex 存储作为属性传递而不是全局访问它?
  • CloneFactory.sealedClone(window.Project.Model.ReportTemplateModel);字段Definition返回的对象还是稍后创建?
  • 如前所述,我还没有真正有机会使用 Vue 做很多事情,所以vue-cli 也是我还没有使用过的东西。一定会检查出来的! @MJ_Wales 我尝试通过window.store... 访问模板内的商店,但收到错误消息,提示“窗口未定义”。关于ReportTemplateModel,是的,它包含一个属性“Defitinition”,刚初始化时它是一个空数组。

标签: vue.js vuejs2


【解决方案1】:

将数据推送到嵌套数组总是会导致组件不更新的问题。

尝试将 getter 添加到您的商店并使用该 getter 从商店获取数据

let store = new Vuex.Store({
  state: {
    t: undefined,
    currentModel: undefined
  },
  mutations: {
    storeNewModel(state) {
      let model = CloneFactory.sealedClone(
        window.Project.Model.ReportTemplateModel);
      model.Header = CloneFactory.sealedClone(
        window.Project.Model.ReportTemplateHeaderModel);
      state.currentModel = model;
    },
    storeNewModelDefinition(state, definition) {
     state.currentModel.Definition.push(definition);
    }
  },
  getters:{
    definitions(state){
      return state.currentModel.Definition
    }
  }
});

在你的组件中,你可以添加一个从 store 中获取数据的计算属性

Vue.component('report-summary', {
  props: ['translation', 'store'],
  computed: {
    definitions(){
     return store.getters.definitions
    }
  },
  template: '<div>
    '<div class="alert alert-secondary" role="alert">' +
    '<h5 class="border-bottom border-dark"> {{ translation.currently_defined }}</h5>' +
    '<div>{{ definitions.length }} {{translation.elements }}</div>' +
    '</div>' +
    '</div>'
});

更新(2019 年 11 月 30 日) 如果上面的代码仍然不起作用,请将您的 storeNewModelDefinition 突变更改为:

storeNewModelDefinition(state, definition) {
     // Create a new copy of definitions
     let definitions = [...state.currentModel.Definition]

     // Push the new item
     definitions.push(definition)

     // Use `Vue.set` so that Vuejs reacts to the change
     Vue.set(state.currentModel, 'Definition', definitions);
    }

【讨论】:

  • 只是一个错字,应该是 definitions.length 而不是 definition.length(注意 S)
  • 我试了一下,getter 和 computer 属性在组件初始化时被调用,但在其底层数据刷新后没有被调用。仍然是一个有用的例子,谢谢。
  • @SaschaM78 definitions getter 中有错字。使用currentMode 代替currentModel。我现在已经修好了。我希望你在你的代码中修复它。如果它仍然不起作用,那么,我建议再进行一次更改。请在答案中查看我的更新
  • 是的,我已经看到了这个并且已经在我的代码中改变了它。我也会测试你更新的代码,非常感谢你的努力!
【解决方案2】:

只是对您的 EventBus 实验的说明(您绝对应该尝试解决反应性问题,而不是尝试像 $forceUpdate 这样的技巧)

您对 EventBus 的实验没有成功的原因是使用 匿名函数 作为事件处理程序。看看这段代码:

  methods: {
    onEvent() {
      console.log(this)
    }
  },
  mounted() {
    // this.onEvent is method where "this" is bound to current Vue instance
    this.$bus.$on("test", this.onEvent);
    // Handler is anonymous function - "this" refers to EventBus Vue instance
    this.$bus.$on("test", function() {
      console.log(this)
    });
  }
  • 1t 事件处理程序是一个函数,其中this 被显式(由 Vue 本身)绑定到处理程序“存在”的 Vue 实例
  • 第二个事件处理程序是匿名函数,this 没有显式绑定,所以它最终像this == event bus Vue instance。你可以像这样使用闭包来解决这个问题:
  mounted() {
    let self = this;
    this.$bus.$on("test", function() {
       // self = "this" in context of function "mounted"
       console.log(self)
    });
  }

this 在 JS 中可能很棘手...

【讨论】:

  • 我的宿敌this。是的,在这种情况下this 不引用组件是完全有道理的。如您的示例所示,我修复了我的代码以使用 EventBus,现在只要定义更改,组件就会更新。我仍然会尝试找到比致电$forceUpdate 更好的方法,但现在它就像一个魅力。谢谢。
  • 我不同意应该使用事件总线来通知组件有关 Vuex 状态的变化。这就像用枪杀死蚊子一样。我要求不要将其标记为可接受的答案,因为它可能会误导社区中即将到来的开发人员。
  • 完全同意。这不应该是一个公认的答案。我只是想解释事件总线代码的问题......
  • Michal,@AnkitKante,这个答案现在解决了我的问题,这就是我将其作为解决方案进行检查的原因。我现在取消选中它,并将继续您在答案中提供的更新,看看我是否可以以正确的方式解决我的问题。
【解决方案3】:

来自 vuejs 文档 Reactivity in depth 部分

数据对象中必须存在一个属性,以便 Vue 对其进行转换并使其具有响应性

这是一次性的 vue 反应性警告。 Vue 无法检测到属性添加或删除。

在你的状态而不是currentModel: undefined 添加你想要反应的所有属性,比如

currentModel: { definitions:[] }

那么任何从definitions 数组中添加或删除的元素都会自动变为响应式。

您可以在文档中找到更多解释

Reactivity in depth

Change detection caveats

【讨论】:

  • 感谢您的回答和有用的链接。我已经在我的测试中添加了一个初始的空数组definitions 属性,但当时它仍然没有解决问题(但可能是我当时的代码中有其他问题导致它无法按预期工作)。
  • 是的,向已经分配给组件数据的对象添加新属性是有问题的,但是如果您的组件已经具有反应性属性 currentModel 并且您将新对象分配给它(就像他所做的那样),则整个对象都已生成反应性的。因此,在data 中定义对象的整个“形状”并不重要。您的对象必须是“完整的”(就形状而言)您将其分配到现有的 data 属性....
猜你喜欢
  • 2022-09-28
  • 1970-01-01
  • 2018-04-22
  • 1970-01-01
  • 2019-02-26
  • 2021-03-03
  • 2018-11-08
  • 2021-08-31
  • 2021-07-26
相关资源
最近更新 更多