【问题标题】:How vuejs knows the depenedencies of computed property for caching?vuejs 如何知道计算属性对缓存的依赖关系?
【发布时间】:2017-01-09 07:30:57
【问题描述】:

我有这个 Vue.js 代码:

new Vue({
  data:{
         myValue:'x',
         myOtherValue:'y'
  },
  computed: {
       myComputed: myFunction(){
          return this['my' + 'Value']
       }
  }
})

如您所见,计算的属性将被缓存,并且仅依赖于data.myValue。我的问题是 Vue.js 缓存系统如何知道只有在 myValue 更改时才再次运行计算函数?

如果我更改myOtherValue 变量,myComputed 函数将使用缓存,我不会再次调用它。

我想了几种方法来实现它。但是 Vuejs 是怎么做到的呢? 我已阅读这篇文章:https://vuejs.org/v2/guide/computed.html 并没有找到答案。

这段代码中发生了什么,它将依赖什么?

const flag=2
new Vue({
  data:{
         myValue:'x',
         myOtherValue:'y'
  },
  computed: {
       myComputed: myFunction(){
          if (flag==1){
              return this['my' + 'Value']
          }
          else
              return this['my' + 'Other' + 'Value']
       }
  }
})

奖励:我将不胜感激我在 VueJS 代码中链接到相关函数:https://github.com/vuejs/vue

【问题讨论】:

标签: javascript vue.js vuejs2


【解决方案1】:

我将只解决具体问题vue.js 如何知道哪些依赖项会影响哪些计算属性?

简单的答案是,每次 vue 评估一个计算属性时,它都会创建一个映射,该映射包含在该调用范围内访问的所有反应性属性。下次当这些反应属性中的任何一个发生变化时,它们将触发对计算属性的重新评估。

如果在计算属性的最近一次评估期间,它的反应性依赖项之一从未达到(可能是因为它位于 if/else 构造的非旅行路径中),则对该反应性属性的后续更改将不会触发重新评估计算的属性。

通过修改 fiddle 中的两个反应属性来观察此行为(只需在相应的输入框中键入)。需要注意的几点:

  • called 计算属性在文档加载时评估一次(它被触发,因为它在模板中呈现)。
  • 因为path 设置为1,所以将被映射为依赖项的反应属性是val1。因此,它将是唯一可以在 called 更改时触发重新评估的方法。 val2 的值也可以更改,但不会对 called 产生相同的影响,即使它明确存在于函数中。
  • 当您单击“更改路径”按钮时,path 会从 1 切换到 2
  • 在路径切换之后,请注意对val1 的更改只会再次影响called。因为在上次重新评估之前path 已设置为2,所以val1 将无法访问,并且不会再被映射为called 的依赖项。从那时起,对其值的后续更改不会触发对called 的重新评估。但是,val2 现在已被映射为 called 的依赖项,并且对它的更改会触发重新评估,就像他们之前对 val1 所做的那样。直到下一条路径从 2 切换回 1 之前都是如此。

这是代码。

let path=1
let count=0
const vm=new Vue({
  el:"#app",
  data:{
         val1:null,
         val2:null,
  },
  computed: {
       called: function(){

            if (path==1){
              this.val1
            }
            if (path==2){
                this.val2
            }
            return "I was just called "+ ++count +" times"
       }
  },
  methods: {
    changePath(){
        path = path==2 ? 1 : 2
    }
  }
})

及对应的模板

<div id="app">
  <input v-model="val1"/> {{val1}}
  <br>
  <input v-model="val2"/> {{val2}}
  <br>
  <button @click="changePath">change path</button>
  <br>
  {{ called }}
</div>

【讨论】:

  • 我觉得这应该是公认的答案。我很欣赏 CodinCat 分享原始代码的链接,但我更希望有人能提供其工作方式的要点。
  • 这清楚地解释了“错误计数”是如何发生的。你对如何避免它有什么建议?
  • @Yiping 在我看来,解决方案是确保计算函数始终包含顶层的依赖属性。在上面的例子中,path 不应该在全局范围内(在 Vue 的职责范围之外),而应该在 data 中,其中使用 setter 和 getter 来监视其状态。如果data.path 发生变化,它将触发called() 的重新运行,然后它将看到this.val2 的变化。如果 this.val2 位于函数的顶层(不在 if 内部),called() 将在它发生更改时被执行。
  • @Yiping 本质上,如果您希望计算函数注册特定的依赖项,则需要确保每次调用计算函数时都可以访问它们。作为一个简单的技巧,您可以在函数的开头列出它们。顶部的this.val1; this.val2 将对它们中的任何一个进行更改,重新触发对计算函数的调用。
  • 更多解释它是如何工作的here,由this forum post指出,链接到这里。
【解决方案2】:

这是 Vue.js 的响应式系统,而不是缓存系统。

组件中的数据将被转换为 getter 和 setter。当你通过 getter 访问一个值时,getter 会将它添加到依赖项中,当你通过 setter 修改值时,setter 会通知所有依赖该值的人。

这里是源代码,所有的魔法都发生在这个函数中:https://github.com/vuejs/vue/blob/dev/src/core/observer/index.js#L131

【讨论】:

  • 如果这是机制。这将是越野车。在第二个例子中。如果我将flag 值更改为1,我将在 (flag==2) 时得到缓存答案,而不是新答案。
  • 是的,这是一个有趣的问题。因为flag 没有反应,所以myComputed 不会知道flag 的变化。这是演示:codepen.io/CodinCat/pen/rjOjjQflag 将在 1 秒后更新,但计算属性不会对标志做出反应
  • myComputed 只会在 10 秒后更新,因为您触摸了myOtherValue。如果将间隔更改为this.myValue += 1myComputed 将永远不会更新,因为myValue 不是myComputed 的依赖项
  • 所以正确的做法是在该组件的数据中添加flag,然后将其转换为反应性属性,并成为myComputed的依赖项,然后一切正常。
  • 这是另一个工作示例:codepen.io/CodinCat/pen/vgNgQy,以防您想在组件之外拥有一个变量。它必须是一个对象,当您将该对象提供给data 时,它将被转换为反应性属性,因此myComputed 可以正确地将其收集为依赖项
【解决方案3】:

从文档中可以看出: 计算的属性被缓存,并且仅在响应性依赖项更改时重新计算。 然而,下面的小提琴显示了一些不同的东西。

  1. https://jsfiddle.net/z11fe07p/267/

如果您将标志设置为 2,则计算属性将在您更改 myOtherValue 时重新评估并执行,但是如果标志设置为 1,则不会发生这种情况。我认为它会跟踪你的 if 条件。

在文档中,您通常可以找到相关源代码的链接。 这是计算属性的代码:

【讨论】:

    猜你喜欢
    • 2018-03-26
    • 2016-08-28
    • 2018-09-27
    • 1970-01-01
    • 2018-07-02
    • 2017-02-18
    • 2010-10-06
    • 2020-05-17
    相关资源
    最近更新 更多