【问题标题】:vue.js method return keeps updatingvue.js 方法返回不断更新
【发布时间】:2019-02-05 06:28:04
【问题描述】:

我是 Vue js 的新手,很抱歉这个愚蠢的问题。我正在使用 v-data-table 循环遍历数组。我需要一个方法的返回值不改变,但是当方法运行时它会改变每一行。

我尝试了一个计算值,但由于某种原因你不能将变量传递给计算字段?

<template>
  <div>
    <v-data-table
      :items="responseData"
      class="elevation-1"
    >
      <template slot="items" slot-scope="props">
        <td :key="props.item.tmdbId" class="text-xs-right">
          This: {{checkMovieExists(props.item.tmdbId)}}
        </td>
      </template>
    </v-data-table>
  </div>
</template>

<script>
import axios from "axios";
export default {
  data() {
    return {
      allMovies: []
    };
  },
  mounted() {
    axios.get("XXXX")
      .then(response => (this.allMovies = response.data)) 
  },
  methods: {
    checkMovieExists(strMovieTmdbId){
      this.allMovies.forEach(movie => {
        if (movie.tmdbId == strMovieTmdbId) {
          return "Exists"
        }
      });                
    }
  }
}
</script>

【问题讨论】:

  • “我需要一个方法的返回值不改变” 你这是什么意思?您是否希望该方法只为每个项目触发一次,然后在项目的数据发生变化时不再触发?
  • checkMovieExists 函数本质上返回 undefined - forEach 的结果。如果您想查找项目,请改用Array.find
  • 另外,您将responseData 绑定到数据表组件的items 属性,但您没有在任何地方定义responseData...
  • 抱歉 ResponseData 实际上来自已删除的代码的另一部分。这基本上会根据搜索返回电影列表。我想要的是在运行时触发一次的方法,如果这有意义的话?所以每一行都会有不同的值。
  • 我仍然不确定我是否理解您到底想要完成什么。正如raina77ow 提到的,第一个明显的问题是您的checkMovieExists 方法没有返回任何内容,因此在模板中的该方法调用中不会呈现任何内容。这看起来确实是您应该使用计算属性的东西(通常直接调用模板中的方法而不是使用计算属性是一种代码气味)。我建议编辑您的代码示例,使其成为您所面临问题的mcve

标签: javascript vue.js


【解决方案1】:

计算属性仅基于内部状态计算。它们是特殊的,因为它们只需要在它们所依赖的内部状态发生变化时重新计算。他们不接受任何参数,因为这会使他们不仅仅依赖于您的状态的反应性,这会破坏这种缓存机制。您可以使用计算属性来处理您从 api 获得的数据并对其进行循环,但现在让我们忽略它。

正如 cmets 中所指出的,您的方法的问题是您正在使用带有函数的 forEach 循环,并在内部函数中返回。外部函数不返回任何内容,因此返回值为undefined。有几种方法可以做到这一点,但我认为使用 Array.prototype.some 最适合你的情况。您向此方法传递一个函数,该函数为您调用它的数组中的每个项目执行。如果这些调用中的任何一个返回true,则整个事件返回true,否则返回false

movieExists(strMovieTmdbId){
  return this.allMovies.some(movie => movie.tmdbId === strMovieTmdbId);
}

你会注意到我在这里返回一个布尔值。我这样做是因为您的函数称为XYZExists。我们可以使用这个布尔响应在模板中输出适当的内容。

  <template slot="items" slot-scope="props">
    <td :key="props.item.tmdbId" class="text-xs-right">
      This: {{ checkMovieExists(props.item.tmdbId) ? 'Exists' : '' }}
    </td>
  </template>

就作用域插槽的使用而言,我相信只要this.allMovies 类似于:

[
  {
    tmdbId: 1
  },
  {
    tmdbId: 2
  }
]

请记住,items 插槽会渲染一整行,而不仅仅是一个单元格。你需要用&lt;tr&gt; ... &lt;/tr&gt;包围它。


正如Thanksd 在下面评论的那样,如果模板被大量重新渲染,使用此方法可能会导致性能问题。毕竟,每次调用该方法时,它都会查看整个数组,查找该数组中的每一项。

相反,我们可以使用计算属性来准备数据。由于您只在挂载钩子中分配一次数据,因此您的计算属性应该只计算一次。当然,您应该避免更改 this.allMovies 以防止重新计算您的计算属性。

您可以保留我们之前创建的方法,但不是使用this.allMovies 来渲染数据,而是使用此方法来计算数据......一次。我们使用映射来创建一个包含方法结果的额外属性。

computed: {
  preparedData () {
    return this.responseData.map(
      row => {
        return {
          ...row,
          exists: this.movieExists(row.tmdbId)
        }
      }
    );
  }
}

现在,我们不使用responseData(不管是什么),而是使用preparedData 来渲染表格。由于我们现在在每一行上都有一个预先计算的属性exists,我们可以只检查props.item.exists 而不必调用函数。当模板重新渲染时,只要responseData保持不变,我们就使用preparedData的缓存版本。

<v-data-table
  :items="preparedData"
  class="elevation-1"
>
  <template slot="items" slot-scope="props">
    <td :key="props.item.tmdbId" class="text-xs-right">
      This: {{ props.item.exists ? 'Exists' : '' }}
    </td>
  </template>
</v-data-table>

【讨论】:

  • 这可能是他们正在寻找的正确方向。但是,您的解决方案仍然从模板中为数组中的每个项目调用一个方法(我仍然不清楚这是否是他们试图阻止的,但无论哪种方式都是代码异味)。这意味着任何时候绑定到模板的 any 数据发生更改,都会为每个项目触发 checkMovieExists 方法。如果allMoviesresponseData 非常大,这可能会引入性能问题。最好有一个计算对象,它将 tmdbID 映射到布尔值,以确定它们是否存在于 allMovies 数组中。
  • 或者我认为更好的办法是创建一个作为responseData 副本的计算对象,但为每个项目计算一个exists 属性。然后将其传递给表组件。像这样:jsfiddle.net/vdero1j3
  • 根据您的反馈,我稍微扩展了答案。
  • 这很奏效,非常感谢您花时间解释它:-D
猜你喜欢
  • 1970-01-01
  • 2012-04-14
  • 1970-01-01
  • 2021-12-12
  • 2011-02-12
  • 2015-08-02
  • 1970-01-01
  • 2015-02-15
  • 1970-01-01
相关资源
最近更新 更多