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。 观察者建立对dataawesomeAnimalIds字段的依赖。 超时后,awesomeAnimalIds 会更新,这会调用 awesomeAnimalIds 设置器。 由于模板依赖于收到通知的data 字段,因此会触发重新渲染。 重复步骤 (3)。

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

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

有一个常见的误解,即从模板调用方法时“只调用一次”或“即发即弃”。显然并非总是如此,因为方法可以建立反应性依赖

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

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

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

【讨论】:

【参考方案2】:

不,方法不是反应式的。在 Vue 中,只有数据可以是响应式的。

但是了解 Vue 的工作原理很重要...

    Vue 将获取您的模板,将其编译为渲染函数并运行渲染函数 当渲染函数运行时,Vue 会监控所有data() 成员(它会一直这样做,不仅在第一次渲染期间)。如果在渲染期间访问了任何数据,Vue 知道该数据成员的内容会影响渲染结果。 Vue 使用在第 2 步中收集的知识。每当在渲染更改期间“触及”数据成员时,它就知道应该重新渲染并执行此操作...

直接引用数据成员没关系,在computedmethod中使用。如果在渲染过程中“触摸”了数据,则数据将在未来触发重新渲染...

【讨论】:

【参考方案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 中的方法是响应式的吗?的主要内容,如果未能解决你的问题,请参考以下文章

数组中哪些方法是响应式的

为啥这个 Vue 计算属性不是响应式的?

vue2.0 是如何实现响应式的?

Vue 深入响应式原理

实现vue2.0响应式的基本思路

关于数组的响应式方法和非响应式方法