此学习教程是对官方教程的解析,
本章节主要涉及到的官方教程地址:
列表渲染方式
数组渲染
我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。
在 v-for 块中,我们可以访问所有父作用域的 property。v-for 还支持一个可选的第二个参数,即当前项的索引。
第一个的参数:item 被迭代的数组元素
第二个的参数:index 作为索引
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
<script>
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
</script>
</body>
</html>
运行结果:
数组更新检测
变更方法
变更方法:会变更调用了这些方法的原始数组。
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
非变更方法:替换数组
非变更方法:不会变更原始数组,而总是返回一个新数组。
例如 filter()、concat() 和 slice()
当使用非变更方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
对象渲染
你也可以用 v-for 来遍历一个对象的 property。
第一个的参数:value 对象的 property值
第二个的参数:name 对象的 property 名称 (也就是键名)
第三个的参数:index 作为索引
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<ul id="v-for-object">
<li v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</li>
</ul>
<script>
var example2 = new Vue({
el: '#v-for-object',
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
})
</script>
</body>
</html>
运行结果:
整数渲染
v-for 也可以接受整数。在这种情况下,它会把模板重复对应次数。
第一个的参数:item 第几次重复,以1开始
第二个的参数:index 作为索引,以0开始
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<ul id="example">
<li v-for="(item,index) in 10">{{index}}.{{item}}</li>
</ul>
<script>
var example2 = new Vue({
el: '#example'
})
</script>
</body>
</html>
运行结果:
key: 跟踪每个节点的身份,从而重用和重新排序现有元素
列表数据更新后的渲染模式
默认的高效模式
如果数据项的顺序被改变,Vue 默认将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。但只适用于 不 依 赖 子 组 件 状 态 或 临 时 D O M 状 态 ( 例 如 : 表 单 输 入 值 ) 的 列 表 渲 染 输 出 。 \color{red}{不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。} 不依赖子组件状态或临时DOM状态(例如:表单输入值)的列表渲染输出。
特点:
- 高效:因为没有key区分是否独立, 会复用相同标签的子组件, 所以只更新元素而不是重新渲染
- 不绑定子组件状态:同样的,因为没有key区分是否是独立的, 所以子组件状态和具体某个子组件并不绑定。这意味着数据变化视图更新时,具体某个子组件的状态不会跟着这个组件。这往往造成BUG。
使用key模式: 为每项提供一个唯一 key attribute
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素。
特点:
- 并不高效:每个子组件都有唯一key区分, 所以复用不高,而且有维护唯一key与具体子组件关联的开销。
- 绑定子组件状态:因为绑定了,所以数据变化视图更新时,具体某个子组件的状态会跟着这个组件。
选择哪种模式
基于两个模式完全相反的特点,所以官方建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
两种模式效率对比
例子(使用和不使用v-bind:key来比较):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<ul id="example">
<li v-for="(item, index) in items"><!-- v-bind:key="item.id" -->
{{ index }} - id[{{ item.id }}] - message[{{ item.message }}]
</li>
</ul>
<script>
var max_num = 10000;
var items = [];
for(var i=0; i<max_num; i++){
var im = {};
im["id"] = ""+i;
im["message"] = "Foo";
items.push(im);
}
var beforeUpdateTime = null;
var updatedTime = null;
var vm = new Vue({
el: '#example',
data: {
items: items
},
beforeUpdate: function () {
beforeUpdateTime = new Date().getTime();
console.log("数据更新前时间:"+beforeUpdateTime);
},
updated: function () {
updatedTime = new Date().getTime();
console.log("数据更新后渲染完成时间:"+updatedTime);
console.log("耗时:"+(updatedTime-beforeUpdateTime)+"ms");
}
})
</script>
</body>
</html>
不使用key:
使用key:
可以看到不使用key的默认模式高效的多。
两种模式子组件状态绑定的区别
例子(使用和不使用v-bind:key来比较):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>
<input type="text" v-model="name">
<button @click="add">添加</button>
</div>
<ul>
<li v-for="(item, i) in list"><!-- v-bind:key="item.id" -->
<input type="checkbox"> {{item.name}}
</li>
</ul>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
name: '',
newId: 3,
list: [
{ id: 1, name: 'a' },
{ id: 2, name: 'b' },
{ id: 3, name: 'c' }
]
},
methods: {
add() {
//注意这里是unshift
this.list.unshift({ id: ++this.newId, name: this.name })
this.name = ''
}
}
});
</script>
</body>
</html>
不使用key:
先选中选项b后,在列表数据头部添加选项d, 视图更新后, 选项b的选中状态变到了a上
使用key:
先选中选项b后,在列表数据头部添加选项d, 视图更新后, 选项b仍然维持选中状态
可以看到没有key不绑定子组件状态, 而有key绑定子组件状态。
在自定义组件上使用 v-for
两个约束:
-
当在组件上使用 v-for 时,key 是必须的
-
任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要使用v-bind指令和组件的props进行绑定:
例子:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<ul id="app">
<my-component
v-for="(im, i) in items"
v-bind:item="im"
v-bind:index="i"
v-bind:key="im.id"
></my-component>
</ul>
<script>
Vue.component('my-component', {
template: '<li>{{ index }} - id[{{ item.id }}] - message[{{ item.message }}]</li>',
props: ['item', 'index']
})
var vm = new Vue({
el: '#app',
data: {
items: [
{ id: 1, message: 'Foo1' },
{ id: 2, message: 'Foo2' },
{ id: 3, message: 'Foo3' }
]
}
});
</script>
</body>
</html>
运行结果:
v-for 与 v-if 一同使用
处于同一节点,v-for 的优先级比 v-if 更高,即:
先
执
行
v
−
f
o
r
循
环
,
再
执
行
v
−
i
f
判
断
\color{red}{先执行v-for循环, 再执行v-if判断}
先执行v−for循环,再执行v−if判断
如果想先判断再循环,将 v-if 置于v-for所在元素的外层元素 (或 <template>) 上
本章节教程结束。
全部教程地址:Vue入门实战教程 | 寒于水学习网
特定模板语法实战: