问题描述
我使用 Vue 已经有一段时间了,我的经验一直是,如果更新了底层响应式数据,方法将重新计算.我在 SO 上遇到了相互矛盾的信息:
- 我试图回答
为组件实例创建了一个特殊的观察器,以确定何时需要重新渲染.
Vue 将
data
的所有属性转换为 getter 和 setter.
get Animal() {//添加依赖:如果动物更新,可能会触发重新渲染...}设置动物(){//通知观察者动物已经更新...}获得 awesomeAnimalIds() {//添加依赖:如果 awesomeAnimalIds 更新,可能会触发重新渲染...}设置 awesomeAnimalIds() {//通知观察者 awesomeAnimalIds 已更新...}
- 模板已呈现.在渲染期间,从模板调用
isAwesome
. - 在
isAwesome
的主体中,调用了awesomeAnimalIds
的getter. - 观察者建立对
data
的awesomeAnimalIds
字段的依赖. - 超时后,更新
awesomeAnimalIds
,从而调用awesomeAnimalIds
设置器. - 因为模板依赖于接收通知的
data
字段,所以会触发重新渲染. - 重复步骤 (3).
从这个和上面的例子,我们可以得出以下结论:
从模板进行的方法调用建立了对方法调用堆栈中使用的
data
字段子集的反应式依赖.如果底层字段被更新,它将触发组件的重新渲染.有一个普遍的误解,认为方法只调用一次".或火而忘记"从模板调用时.显然情况并非总是如此,因为方法可以建立反应式依赖.
那么我们什么时候应该使用计算属性和方法呢?
请参阅关于计算缓存与方法一>.这是我的看法:
- 计算属性只会在其响应式依赖项发生变化时重新评估.IE.它使用缓存来提高效率.
- 计算属性应该没有副作用.例如.你不应该从他们那里调用
fetch
. - 如果可能,出于效率原因,始终更喜欢计算属性而不是方法.
- 如果您有副作用或需要传入参数(如原始问题中所示),请使用方法.
I've been using Vue for a while, and my experience has always been a method will recompute if its underlying reactive data is updated. I've encountered conflicting information on SO:
- I was attempting to answer this question, and was told multiple times that this isn't the case.
- The accepted answer here indicates that "[a method] will only be evaluated when you explicitly call it."
I searched through the docs and I didn't see anything incredibly clear.
If they are not reactive, then why does this example work?
<ul> <li v-for="animal in animals" :key="animal.id"> <span v-if="isAwesome(animal)">{{ animal.name }}</span> </li> </ul>
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); }, }, };
I would really like to have a definitive and satisfying answer that this community can refer to.
解决方案Based on How Changes Are Tracked from the docs, here's what's going on:
A special watcher is created for the component instance to determine when a re-render is required.
Vue converts all properties of
data
to getters and setters.
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 ... }
- The template is rendered. During the render, a call to
isAwesome
is made from the template. - In the body of
isAwesome
, the getter forawesomeAnimalIds
is invoked. - The watcher establishes a dependency on the
awesomeAnimalIds
field ofdata
. - After a timeout,
awesomeAnimalIds
is updated, which invokes theawesomeAnimalIds
setter. - Because the template depends on a
data
field which received a notification, a re-render is triggered. - Repeat step (3).
From this and the example above, we can conclude the following:
There is a common misconception that methods are "invoked only once" or "fire and forget" when called from a template. This is clearly not always the case because methods can establish a reactive dependency.
So when should we use a computed property vs a method?
See the guide section on Computed Caching vs Methods. Here's my take on it:
- A computed property will only reevaluate when its reactive dependencies have changed. I.e. it uses caching for improved efficiency.
- Computed properties should be side-effect free. E.g. you shouldn't call
fetch
from them. - Always prefer a computed property to a method if possible for efficiency reasons.
- Use a method if you have side effects or if you need to pass in an argument (as seen in the original question).
这篇关于Vue 中的方法是反应式的吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!