【第五节】列表渲染

文章目录

列表渲染

v-for

使用 v-for 指令基于一个数组渲染一个列表。这个指令使用特殊的语法,形式为 item in items ,items是数据数组, item 是当前数组元素的别名:

另外,你可以为索引指定一个别名(如果 v-for 用于一个对象,则可以为对象的键指定一个别名)

c 复制代码
<div v-for="(index,item)in items">
{{ index}}{{ item.message }}
</div>

从 1.0.17 开始可以使用 of 分隔符,更接近 JavaScript 遍历器语法

HTML

c 复制代码
<div v-for="item of items"></div>

template v-for

类似于 template v-if ,也可以将 v-for 用在 标签上,以渲染一个包含多个元素的块。例如:

c 复制代码
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider"></li>
</template>
</ul>

数组变动检测

Vue.js 包装了被观察数组的变异方法,故它们能触发视图更新。被包装的方法有:

push()

pop()

shift()

unshift()

splice()

sort()

reverse()

打开浏览器的控制台,用这些方法修改上例的items 数组。例如:example1.items.push({ message:'Baz'})

变异方法 替换数组

变异方法,如名字所示,修改了原始数组。相比之下,也有非变异方法,如filter(),concat()slice(),不会修改原始数组而是返回一个新数组。在使用非变异方法时可以直接用新数组替换旧数组:

c 复制代码
example1.items =example1.items.filter(function(item){return item.message.match(/Foo/)

可能觉得这将导致 Vue.js 弃用已有 DOM 并重新渲染整个列表--幸运的是并非如此。Vue.js 实现了一些启发算法,以最大化复用 DOM 元素,因而用另一个数组替换数组是一个非常高效的操作。

track-by

有时需要用全新对象(例如通过 API调用创建的对象)替换数组。因为 v-for默认通过数据对象的特征来决定对已有作用域和 DOM 元素的复用程度,这可能导致重新渲染整个列表。但是,如果每个对象都有一个唯一ID 的属性,便可以使用 track-by 特性给 vue.js个提示,Vue.js 因而能尽可能地复用已有实例。

例如,假定数据为:

c 复制代码
{
 items :[
	{_uid:'88f869d',...},
	{_uid :'7496c10',...}
	]
	}

然后可以这样给出提示:

HTML

c 复制代码
<div v-for="item in items" track-by="_uid">
<!--content -->
</div>

然后在替换数组 items 时,如果 Vue.js 遇到一个包含 _uid:'88f869d'的新对象,它知道它可以复用这个已有对象的作用域与 DOM 元素。

track-by $index

如果没有唯一的键供追踪,可以使用 track-by="$index"它强制让 v-for 进入原位更新模式:片断不会被移动,而是简单地以对应索引的新值刷新。这种模式也能处理数据数组中重复的值。

这让数据替换非常高效,但是也会付出一定的代价。因为这时 DOM 节点不再映射数组元素顺序的改变,不能同步临时状态(比如元素的值)以及组件的私有状态。因此,如果 v-for 块包含 元素或子组件,要小心使用 track-by="$index"

加了个索引

问题

因为 JavaScript 的限制,Vue.js 不能检测到数组的以下变化:

1.直接用索引设置元素,如 vm.items[0]={};

2.修改数据的长度,如 vm.items.length=8

为了解决问题 (1),Vue.js 扩展了观察数组,为它添加了一个 $set()方法:

c 复制代码
//与example1.items[0]=...相同,但是能触发视图更新example1.items.$set(0,{childMsg:'Changed!'})

至于问题 (2),只需用一个空数组替换 items。

除了 $set(),Vue.js也为观察数组添加了 $remove()方法,用于从目标数组中查找并删除元素,在内部它调用 splice()。因此,不必这样:

c 复制代码
var index= this.items.indexOf(item)
if(index!==-1){
this.items.splice(index,1)
}

只用这样:

c 复制代码
this.items.$remove(item)
c 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="vue.js"></script>
    <style>
    .blue {
        color: blue;
    }
    </style>
</head>

<body>
    <div id="example-1">
        <ul>
            <template v-for="item in items">
                <li>
                    {{$index}}.{{ item.msg }}

                    <button v-on:click="f5(item)">vm.items.splice(index, 1)</button>

                    <button v-on:click="f6(item)">vm.remove</button>
                </li>
            </template>
        </ul>
        <ul>
            <li>
                <button v-on:click="f1">vm.items[0] = {} 失效</button>
            </li>
            <li>
                <button v-on:click="f2">vm.items.$set(0, { childMsg: 'Changed!'}) </button>
            </li>
            <li>
                <button v-on:click="f3">vm.items.length = 0 失效</button>
            </li>
            <li>
                <button v-on:click="f4">vm.items={}</button>
            </li>
        </ul>
        <div class="blue">
            {{$data | json }}
        </div>
        <pre>
        因为 JavaScript 的限制,Vue.js 不能检测到下面数组变化:
        直接用索引设置元素,如 vm.items[0] = {};
        修改数据的长度,如 vm.items.length = 0。
        </pre>
    </div>
    <script>
    var vm = new Vue({
        el: '#example-1',
        data: {
            items: [{
                msg: 'Foo'
            }, {
                msg: 'Bar'
            }, {
                msg: 'George'
            }]
        },
        methods: {
            f1: function() {
                vm.items[0] = {}; // 失效
            },
            f2: function() {
                vm.items.$set(0, {
                    childMsg: 'Changed!'
                })
            },
            f3: function() {
                vm.items.length = 0; // 失效
            },
            f4: function() {
                vm.items = {}
            },
            f5: function(item) {
                var index = this.items.indexOf(item) //Search an array for the item
                if (index !== -1) {
                    this.items.splice(index, 1) //Selects a part of an array, and returns the new array
                }
            },
            f6: function(item) {
                this.items.$remove(item)
            }
        }

    })
    </script>
</body>

</html>

对象 v-for

也可以使用 v-for 遍历对象。除了 $index之外,作用域内还可以访问另外一个特殊变量$key

c 复制代码
<ul id="repeat-object"class="demo">
	<li v-for="value in object">{{ $key }}:{{ value }}			  </li>
</u1>
c 复制代码
new Vue({
	el:'#repeat-object'
	data:{
		object:{
			FirstName:'John'
			'Doe',LastName :
			Age:30
			}
		}
	})

也可以给对象的键提供一个别名

c 复制代码
<div v-for="(key,val)in object">{{ key }}{{ val }}</div>

在遍历对象时,是按 object.keys()的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下是一致的。

值域 v-for

v-for 也可以接收一个整数,此时它将重复模板数次。

c 复制代码
<div>
<span v-for='n in 10">{{n}}</span></div>

结果:

显示过滤/排序的结果

有时想显示过滤/排序过的数组,同时不实际修改或重置原始数据。有两个办法:

1.创建一个计算属性,返回过滤/排序过的数组,

2.使用内置的过滤器 filterBy 和 orderBy 。

计算属性有更好的控制力,也更灵活,因为它是全功能 JavaScript。但是通常过滤器更方便,详细见 API。

  • 内置过滤器