开发者问题收集

为什么我的方法因未知原因被调用?

2019-06-17
60

我有函数 filterData ,可根据多个输入更新图表数据。但是,当不相关的输入发生变化(例如在文本字段中输入)时,每次都会调用该函数,导致图表重新呈现。我已记录输出,但没有任何参数发生变化。

为什么会调用该函数?有没有办法跟踪它,还是我做错了。

<DeviceChart :input-data="filterData(inputData, filters, otherParams)"/>
export default {
  methods: {
    filterData(inputData, inputFilters, otherParameters) {
      console.log('Filter data has been called...')
      ...
      return result;
    }
  }
}

1个回答

首先,介绍一些背景知识。

Vue 模板将被编译为 render 函数。当组件渲染时,将调用此函数并返回 VDOM 节点树。这些 VDOM 节点描述子组件和相应的 DOM。当组件随后更新时,将再次调用此 render 函数,返回新的 VDOM 树。然后,Vue 会比较新旧树并决定要进行哪些更改。

您可以将 render 函数视为与计算属性非常相似。返回类型(VDOM 节点树)可能对您来说有点陌生,但除此之外,它就像您自己编写的任何计算属性一样。它运行、跟踪依赖项并返回一个值。如果这些依赖项随后发生变化,它将再次运行。

重要的是,就像计算属性一样,Vue 并不详细了解您如何在该函数中使用依赖项。对依赖项的任何更改都会导致整个 render 函数重新运行。

这听起来可能很昂贵,但通常不会产生很大的开销。比较 VDOM 节点树通常也是相当便宜的操作。昂贵的部分是更新 DOM,无论您使用哪种方法,都必须这样做。

因此,当您谈论“无关输入”更改时,您需要记住模板(即 render 函数)将始终作为一个整体运行。如果任何依赖项发生变化,则模板中的所有代码都将重新运行。

在您的情况下,您有一个方法调用来过滤 input-data 的值。每次模板运行时都会调用该方法。输入和图表可能是模板内的独立组件,但这并不重要,整个模板都需要运行才能生成新的 VDOM 树。输入背后的数据是模板的依赖项,当它发生变化(由于输入内容而发生)时,模板会重新运行。

但是,子组件不一定会重新渲染。当 Vue 比较父组件节点的 VDOM 树时,它会尝试将新旧树中的子项配对(这就是 key 的用途,提供有关要配对哪些子项的提示)。一旦配对了子项,它将更新任何需要它的子项的 props。如果子项的 props 没有改变,那么该子项就不需要重新渲染自身。

对于您来说,我假设 input-data 是一个数组。每次调用方法 filterData 时,它都会返回一个新数组。就您而言,该数组可能“相同”,但实际上并不是同一个数组。从 JavaScript === 的角度来看,该值已更改。我不知道 DeviceChart 如何处理这些数据,但这很可能是导致其重新渲染的原因。对于 Vue 组件来说,重新渲染是一个廉价的过程,通常不值得担心,但您的图表可能正在使用一些第三方库,重新渲染可能并不那么简单。

最简单的解决方案可能就是对 input-data 使用计算属性。由于计算属性被缓存,因此每次模板运行时您都会获得相同的数组。只有当计算函数的依赖项发生变化时,缓存才会失效,因此模板的其他依赖项不会产生任何影响。

有很多替代方案,但大多数都很麻烦。通常,这些方案涉及将过滤后的值存储在 data 中,并在需要时尝试更新它。如果可以的话,我不建议你尝试任何类似的东西。

如果有循环,计算属性会变得繁琐。

一个可行的替代方案也是与循环配合良好的,那就是为您的图表引入一个包装器组件。如果您将 inputDatafiltersotherParams 作为 3 个单独的 props 传递给该包装器组件,那么执行实际过滤的责任就可以转移到包装器组件中。只要这 3 个 props 不变,当外部模板重新渲染时,包装器就不会重新渲染。我仍然建议在包装器中使用计算属性,但这并不重要,因为模板无论如何都不会运行。

skirtle
2019-06-17