Vue 中的方法是响应式的吗?
Posted
技术标签:
【中文标题】Vue 中的方法是响应式的吗?【英文标题】:Are methods in Vue reactive? 【发布时间】:2020-04-25 09:47:08 【问题描述】:我使用 Vue 已经有一段时间了,我的经验一直是,如果其底层反应数据更新,方法将重新计算。我在 SO 上遇到了相互矛盾的信息:
我试图回答 this question,但多次被告知不是这种情况。 接受的答案here 表示“[a method] 只会在您明确调用它时进行评估。”我搜索了文档,但没有看到任何令人难以置信的清晰内容。
如果它们不是反应式的,那么为什么这个例子有效?
<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);
,
,
;
我真的希望得到这个社区可以参考的明确且令人满意的答案。
【问题讨论】:
好问题——不确定是否有代表现实的“官方”答案! 【参考方案1】:根据文档中的How Changes Are Tracked,这是发生了什么:
为组件实例创建一个特殊的观察者以确定何时需要重新渲染。
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
...
-
模板已渲染。在渲染期间,从模板调用
isAwesome
。
在isAwesome
的主体中,调用awesomeAnimalIds
的getter。
观察者建立对data
的awesomeAnimalIds
字段的依赖。
超时后,awesomeAnimalIds
会更新,这会调用 awesomeAnimalIds
设置器。
由于模板依赖于收到通知的data
字段,因此会触发重新渲染。
重复步骤 (3)。
从这个和上面的例子,我们可以得出以下结论:
从模板进行的方法调用建立了对方法调用堆栈中使用的
data
字段子集的响应式依赖。如果底层字段被更新,它会触发组件的重新渲染。
有一个常见的误解,即从模板调用方法时“只调用一次”或“即发即弃”。显然并非总是如此,因为方法可以建立反应性依赖。
那么我们什么时候应该使用计算属性和方法呢?
请参阅Computed Caching vs Methods 上的指南部分。这是我的看法:
计算属性仅在其反应性依赖项发生变化时才会重新评估。 IE。它使用缓存来提高效率。 计算属性应该没有副作用。例如。你不应该给他们打电话fetch
。
出于效率原因,如果可能,总是更喜欢计算属性而不是方法。
如果您有副作用或需要传入参数(如原始问题所示),请使用方法。
【讨论】:
【参考方案2】:不,方法不是反应式的。在 Vue 中,只有数据可以是响应式的。
但是了解 Vue 的工作原理很重要...
-
Vue 将获取您的模板,将其编译为渲染函数并运行渲染函数
当渲染函数运行时,Vue 会监控所有
data()
成员(它会一直这样做,不仅在第一次渲染期间)。如果在渲染期间访问了任何数据,Vue 知道该数据成员的内容会影响渲染结果。
Vue 使用在第 2 步中收集的知识。每当在渲染更改期间“触及”数据成员时,它就知道应该重新渲染并执行此操作...
直接引用数据成员没关系,在computed
或method
中使用。如果在渲染过程中“触摸”了数据,则数据将在未来触发重新渲染...
【讨论】:
【参考方案3】:这是一个非常有趣的案例。
根据我的阅读和经验,我可以说:不,方法本质上不是反应性的。必须显式调用方法才能执行。
但是,那我该如何解释你的情况呢?我将您的代码放入沙箱中,果然,当您将 id 推送到数组中时,模板会更新以显示动物名称。这将表明一些反应性。什么给了?
嗯,我做了一个实验。我在每个生成随机数的循环中添加了一个简单的div
。
<li v-for="animal in animals" :key="animal.id">
<div> random() </div>
<span v-if="isAwesome(animal)"> animal.name </span>
</li>
...
random()
return Math.random();
而我看到的是,每次一个新的 id 被推入数组时,所有的随机数都会改变。这是理解为什么它“看起来”好像方法 isAwesome
是反应性的关键。
不知何故,当一个新的 ID 被推送到数组时,Vue 会重新渲染整个循环,从而再次执行这些方法。我无法解释为什么 vue 会重新渲染整个循环的内部工作原理,这需要进一步研究。
所以回答你的问题。 isAwesome
不是反应式的,它只是循环重新渲染所产生的一种错觉。
【讨论】:
对于组件中的任何可观察属性(prop / data / computed)都是如此。每当它们被更改时,它都会触发update
,这会导致 re-render
并触发模板中绑定的任何方法以上是关于Vue 中的方法是响应式的吗?的主要内容,如果未能解决你的问题,请参考以下文章