Vue 中的方法是反应式的吗?

Dav*_*don 13 vue.js

我已经使用 Vue 一段时间了,我的经验一直是,如果更新其底层反应性数据,方法将重新计算。我在 SO 上遇到了相互矛盾的信息:

  • 我试图回答这个问题,但多次被告知事实并非如此。
  • 接受的答案在这里指出,“当你明确地调用它的方法[]将只进行评估。”

我搜索了文档,但没有看到任何非常清楚的内容。

如果他们不是反应性的,那么为什么这个例子有效?

<ul>
  <li v-for="animal in animals" :key="animal.id">
    <span v-if="isAwesome(animal)">{{ animal.name }}</span>
  </li>
</ul>
Run Code Online (Sandbox Code Playgroud)
export default {
  data() {
    return {
      awesomeAnimalIds: [],
      animals: [
        { id: 1, name: 'dog' },
        { id: 5, name: 'cat' },
        { id: 9, name: 'fish' },
      ],
    };
  },
  created() {
    setTimeout(() => {
      this.awesomeAnimalIds.push(5);
    }, 1000);
    setTimeout(() => {
      this.awesomeAnimalIds.push(9);
    }, 2000);
  },
  methods: {
    isAwesome(animal) {
      return this.awesomeAnimalIds.includes(animal.id);
    },
  },
};
Run Code Online (Sandbox Code Playgroud)

真的很想有一个明确的、令人满意的答案,这个社区可以参考。

Dav*_*don 6

根据文档中如何跟踪更改,这是发生了什么:

Vue 反应性循环图

  1. 为组件实例创建了一个特殊的观察器,以确定何时需要重新渲染。

  2. Vue 将 的所有属性转换data为 getter 和 setter。

get animals() {
  // Add dependency: potentially trigger a re-render if animals updates
  ...
}
set animals() {
  // Notify the watcher that animals has been updated
  ...
}
get awesomeAnimalIds() {
  // Add dependency: potentially trigger a re-render if awesomeAnimalIds updates
  ...
}
set awesomeAnimalIds() {
  // Notify the watcher that awesomeAnimalIds has been updated
  ...
}

Run Code Online (Sandbox Code Playgroud)
  1. 模板已呈现。在渲染期间,isAwesome从模板调用。
  2. 在 的主体中isAwesomeawesomeAnimalIds调用了 getter for 。
  3. 观察者建立对awesomeAnimalIds字段的依赖data
  4. 超时后,awesomeAnimalIds被更新,它调用awesomeAnimalIds设置器。
  5. 由于模板依赖于data收到通知的字段,因此会触发重新渲染。
  6. 重复步骤 (3)。

从这个和上面的例子,我们可以得出以下结论:

从模板进行的方法调用建立data对方法调用堆栈中使用的字段子集的反应式依赖。如果底层字段被更新,它将触发组件的重新渲染。

有一种常见的误解,认为从模板调用方法时“仅调用一次”或“即发即弃”。显然情况并非总是如此,因为方法可以建立反应式依赖关系

那么我们什么时候应该使用计算属性而不是方法呢?

请参阅有关Computed Caching vs Methods的指南部分。这是我的看法:

  • 计算属性只会在其响应式依赖项发生变化时重新评估。即它使用缓存来提高效率。
  • 计算属性应该是无副作用的。例如,你不应该fetch从他们那里打电话。
  • 如果可能,出于效率原因,始终更喜欢计算属性而不是方法。
  • 如果您有副作用或需要传入参数(如原始问题中所示),请使用方法。