VueJS如何在v-for中使用计算属性

100

我如何在列表中使用计算属性。我正在使用VueJS v2.0.2。

这是HTML代码:

<div id="el">
    <p v-for="item in items">
        <span>{{fullName}}</span>
    </p>
</div>

以下是Vue的代码:

var items = [
    { id:1, firstname:'John', lastname: 'Doe' },
    { id:2, firstname:'Martin', lastname: 'Bust' }
];

var vm = new Vue({
    el: '#el',
    data: { items: items },
    computed: {
        fullName: function(item) {
            return item.firstname + ' ' + item.lastname;
        },
    },
});
5个回答

99

每次循环都不能创建计算属性。理想情况下,这些 items 中的每一个都应该是它们自己的组件,这样每个组件就可以有自己的 fullName 计算属性。

如果您不想创建一个 user 组件,您可以使用一个方法而不是计算属性。您可以将 fullName 直接从 computed 属性移动到 methods 中,然后可以像这样使用它:

{{ fullName(user) }}

另外,顺带一提,如果你发现自己需要将参数传递给一个computed,那么你很可能需要使用一个方法代替。


2
如果您需要多次使用 fullName,而且 fullName 方法是一个复杂的函数,在每次迭代中执行 10 次将非常昂贵,那该怎么办?我认为 @PanJunie 的解决方案在这种情况下会更好。 - RichC
我会在更高的状态下进行此操作。下面的计算方法将在您处理排序版本的项目时出现问题。我当然是在追求安全性。让数据尽可能地可重用对我来说更有意义,因此最好在更高的状态下进行。 - Bill Criswell
我的问题是,数组可以根据选择和取消选择的其他项目而改变,这会触发数组本身的反应性,因此我无法在更高的级别上进行操作,除非可能在那时创建一个过滤器并向数组中的对象添加其他属性(这不是一个可怕的想法,但只是导致需要遍历整个数组两次来完成所有操作 - 我猜两次比十次好)。 - RichC

51

你在这里忽略了一个重要的问题,那就是你的items是一个数组,其中包含所有的项,但computed只是一个单独的fullName,无法表示items中的所有fullName。尝试这样做:

var vm = new Vue({
    el: '#el',
    data: { items: items },
    computed: {
        // corrections start
        fullNames: function() {
            return this.items.map(function(item) {
                return item.firstname + ' ' + item.lastname;
            });
        }
        // corrections end
    }
});

然后在视图中:

<div id="el">
    <p v-for="(item, index) in items">
        <span>{{fullNames[index]}}</span>
    </p>
</div>

Bill所介绍的方法肯定可行,但是我们可以使用计算属性来实现,我认为这比在迭代中使用method更好,特别是当应用程序变得更大时。此外,与method相比,在某些情况下computed具有性能优势:http://vuejs.org/guide/computed.html#Computed-Caching-vs-Methods


我使用:class指令来突出显示一行,这比使用方法更好。 - hitautodestruct
4
这种代码不够便携。如果你想遍历一个已排序的items副本并获取fullName,这将失败。真正需要做的是创建一个“用户”组件,它接受一个用户对象,该对象具有计算属性fullName。 - Bill Criswell
items被排序时,为什么会失败?@BillCriswell - JJPandari
2
如果你使用 v-for="item in sortedVersionOfItems",那么 index 将会出错。 - Bill Criswell

11

我喜欢潘俊杰提出的解决方案。它通过仅一次迭代 this.items(在组件创建阶段)并缓存结果来抓住了问题的核心。这当然是利用计算属性而非方法的关键优势。

computed: {
    // corrections start
    fullNames: function() {
        return this.items.map(function(item) {
            return item.firstname + ' ' + item.lastname;
        });
    }
    // corrections end
}
<p v-for="(item, index) in items">
    <span>{{fullNames[index]}}</span>
</p>

我唯一不喜欢的是,在DOM标记中,fullNames 通过列表索引访问。我们可以通过在计算属性中使用 .reduce() 而不是 .map() 来改进这一点,以创建一个对象,其中每个 item.idthis.items 中都有一个键。

考虑以下示例:

computed: {
    fullNames() {
        return this.items.reduce((acc, item) => {
            acc[item.id] = `${item.firstname} ${item.lastname}`;
            return acc;
        }, {});
    },
},

.

<p v-for="item in items" :key="`person-${item.id}`">
    <span>{{ fullNames[item.id] }}</span>
</p>
注意:上面的示例假设item.id是唯一的,例如来自典型的数据库输出。

1

或许可以加入另一个 v-for 循环遍历只有一项的列表:

<div id="el">
    <p v-for="item in items">
        <template v-for="fullName in [item.firstName + ' ' + item.lastName]">
            <span>{{fullName}}</span>
        </template>
    </p>
</div>

虽然不太好看,但这正是你要找的:一个包含特定值的属性名为fullName的对象。

而且这不仅仅是一种虚荣的功能,因为我们可能需要在多个地方使用该值,例如:

<span v-if="...">I am {{fullName}}</span>
<span v-else-if="...">You are {{fullName}}</span>
<span v-else>Who is {{fullName}}?</span>

我的使用情况是我在v-for循环中构建日期(是的,又一个日历),例如:

<v-row v-for="r in 5">
    <v-col v-for="c in 7">
        <template v-for="day in [new Date(start.getTime() + 24*60*60*1000*((c-1) + 7*(r-1)))]">
            <span>
                Some rendering of a day like {{day.getYear()}} and
                {{day.getMonth()}} etc.
            </span>
        </template>
    </v-col>
</v-row>

(为了简洁起见,我省略了:key="whatever"设置)

我承认最好的方法是将其移动到单独的组件中,但如果我们为每个像这样的两行代码创建一个新组件,并且仅在此处使用该组件,则只会污染另一个命名空间。

也许一个v-let="day as new Date(...)"指令对于这种目的会很方便。


-2

你必须在函数调用中传递参数

 <div id="el">
       <p v-for="item in items">
           <span>{{fullName(item)}}</span>
       </p>
 </div>

“仅代码答案不是高质量的答案”。虽然这段代码可能有用,但您可以通过解释它为什么有效、如何有效、何时应该使用以及其限制是什么来改进它。请编辑您的答案,包括解释和相关文档链接。 - Stephen Ostermiller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接