首先简单回顾下v-for‘指令
<ol id="list_area"> <li v-for="book in books">{{book.name}}</li> </ol> <script type="text/javascript"> new Vue({ el:"#list_area", data:{ books:[ {name:'一年级'}, {name:'二年级'}, {name:'三年级'} ] } }) </script>
(1)v-for指令
v-for
指令根据一组数组的选项列表进行渲染。v-for
指令需要使用 item in items
形式的特殊语法,items
是源数据数组并且 item
是数组元素迭代的别名。
在 v-for
块中,我们拥有对父作用域属性的完全访问权限。v-for
还支持一个可选的第二个参数为当前项的索引。
<ol id="list_area"> <li v-for="(book,index) in books">父级为{{parentMessage}}-{{book.name}}-下标为-{{index}}</li> </ol> <script type="text/javascript"> new Vue({ el:"#list_area", data:{ parentMessage:"parent", books:[ {name:'一年级'}, {name:'二年级'}, {name:'三年级'} ] } }) </script>
也可以用 of
替代 in
作为分隔符,因为它是最接近 JavaScript 迭代器的语法:
<div v-for="item of items"></div>
(2)对象的v-for指令
也可以用 v-for
通过一个对象的属性来迭代。
<!-- 对象的v-for指令 --> <ul id="ul_list"> <li v-for="value in obj">{{value}}</li> </ul> new Vue({ el:"#ul_list", data:{ obj:{ name:'熊大', age:23, sex:'男' } } });
结果:
也可以提供第二个的参数为键名:
<!-- 对象的v-for指令 --> <ul id="ul_list"> <li v-for="(value,key) in obj">键为{{key}},值为{{value}}</li> </ul>
结果:
第三个参数为索引
<!-- 对象的v-for指令 --> <ul id="ul_list"> <li v-for="(value,key,index) in obj">键为{{key}},值为{{value}},索引为{{index}}</li> </ul>
结果:
(3)key(待验证)
当 Vue.js 用 v-for
正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的 track-by="$index"
。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key
属性。理想的 key
值是每项都有的唯一 id。这个特殊的属性相当于 Vue 1.x 的 track-by
,但它的工作方式类似于一个属性,所以你需要用 v-bind
来绑定动态值 (在这里使用简写):
<div v-for="item in items" :key="item.id"> <!-- 内容 --> </div>
建议尽可能在使用 v-for
时提供 key
,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
因为它是 Vue 识别节点的一个通用机制,key
并不与 v-for
特别关联,key
还具有其他用途,以后再做讨论。
(4)数组更新检测
①变异方法
Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新。这些方法如下:
push()--末尾添加
pop()--末尾删除
shift()--开头删除
unshift()--开头添加
splice()--添加、删除、替换
sort()--排序
reverse()--倒序翻转
接下来先简单回顾下数组操作方法
var arr = [0,1,2,3,4];
var arr_push = arr.push(9,89,68);/* 数组末尾添加,返回添加后长度 */
console.log(arr_push+"为push方法后返回的数组长度,"+arr+"为arr数组添加后状态");
var arr_pop = arr.pop(); /* 数组末尾删除,返回删除后长度 */
console.log(arr_pop+"为pop方法后返回的数组长度,"+arr+"为arr数组删除后状态")
var arr_shift = arr.shift();/* 数组开头删除,返回删除后长度 */
console.log(arr_shift+"为shift方法后返回的删除元素,"+arr+"为arr数组删除后状态");
var arr_unshift = arr.unshift(88,77,99);
console.log(arr_unshift+"为unshift方法后返回的数组长度,"+arr+"为arr数组添加后状态");
/* 删除(任意个数):参数1:开始的索引;参数2:删除的长度 */
var arr_splice_cut = arr.splice(1,2);
console.log(arr_splice_cut+"为splice方法后删除的元素,"+arr+"为arr数组删除后状态");
/* 添加(任意个数): 插入起始位置、0(要删除的项数)和要插入的项。 如果要插入多个项,可以再传入第四、第五,一直任意多个项。*/
var arr_splice_add = arr.splice(1,0,666,777,888);
console.log(arr_splice_add+"为空数组,即splice添加返回空数组,"+arr+"为arr数组添加后状态");
/* 替换(任意个数):即删除和插入数量相等项数的综合应用,可以指向指定位置插入任意数量的项,且同时删除任意数量的项,
只需要指定3个指定参数:起始位置、要删除的项数和要插入的任意数量项。 插入的项数是不必与删除的项数相等。 */
var arr_splice_replace = arr.splice(0,2,"替换1","替换2");
console.log(arr_splice_replace+"为splice方法后替换掉的元素,"+arr+"为arr数组替换后状态");
var arr_sort = arr.sort();
console.log(arr_sort+"为sort方法后排序");
var arr_reverse = arr.reverse();
console.log(arr_reverse+"为reverse后翻转");
验证:
打开控制台,用前面例子的 books数组调用变异方法:
push后返回添加后的数组长度
②替换数组
变异方法 (mutation method),顾名思义,会改变被这些方法调用的原始数组。相比之下,也有非变异 (non-mutating method) 方法,例如:filter()
, concat()
和 slice()
。这些不会改变原始数组,但总是返回一个新数组。
接下来呢先简单回顾下
/* filter筛选 */
var arr_repeat = ['A','B','A','B','B','C','A','D'];
var arr_filter = arr_repeat.filter(function(element,index,self){
return self.indexOf(element) == index;
})
console.log(arr_filter);//['A','B','C','D'];
console.log(arr_repeat);//['A','B','A','B','B','C','A','D']
/* concat拼接 */
var arr_concat1 = ['O','P','Q'];
var arr_concat2 = arr_concat1.concat(['R','S','T']);
console.log(arr_concat1);//['O','P','Q']
console.log(arr_concat2);//["O", "P", "Q", "R", "S", "T"]
/* slice截取:包含开头不含结尾 */
var arr_slice1 = ['H','I','J','K','L'];
var arr_slice2 = arr_slice1.slice(1,3);
console.log(arr_slice1);// ["H", "I", "J", "K", "L"]
console.log(arr_slice2);// ["I", "J"]
当使用非变异方法时,可以用新数组替换旧数组:
<ol id="list_area"> <li v-for="(book,index) of books"> 父级为{{parentMessage}}-{{book.name}}-下标为-{{index}} </li> </ol> var list_area = new Vue({ el:"#list_area", data:{ parentMessage:"parent", books:[ {name:'一年级'}, {name:'二年级'}, {name:'三年级'} ] } }); list_area.books = list_area.books.filter(function(book){ return book.name.match(/二年级/) });
你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是,事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的、启发式的方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
③注意事项
由于 JavaScript 的限制,Vue 不能检测以下变动的数组:
- 当你利用索引直接设置一个项时,例如:
vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:
vm.items.length = newLength
举个例子:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
接下来挨个看下,为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue
相同的效果,同时也将触发状态更新:
Vue.set(list_area.books,1,{name:"新生班"})
/* 语法结构:Vue.set(vm.items, indexOfItem, newValue) */
list_area.books.splice(0,1,{name:'new新生班'})
/* Array.prototype.splice */
/* 语法结构:vm.items.splice(indexOfItem, 1, newValue) */
也可以使用 vm.$set
实例方法,该方法是全局方法 Vue.set
的一个别名:
list_area.$set(list_area.books,1,{name:"新生班"})
/* 语法:vm.$set(vm.items, indexOfItem, newValue) */
为了解决第二类问题,可以使用 splice
:
list_area.books.splice(4)
/* vm.items.splice(newLength) */
(5)对象更新检测
对象属性改动时,vue可以实时监听检测,实现响应式。但是注意:由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除
var vm = new Vue({
data: {
a: 1
}
})
/* vm.a现在是响应式的 */
vm.b = 2
/* vm.b不是响应式的 */
对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value)
方法向嵌套对象添加响应式属性。例如,对于:
var ul_list = new Vue({
el:"#ul_list",
data:{
message:"信息",
obj:{
name:'熊大',
age:23,
sex:'男'
}
}
});
可以添加一个新的 height身高属性到嵌套的对象:
Vue.set(ul_list.obj,'height','180');
此时,结果为
还可以使用 vm.$set
实例方法,它只是全局 Vue.set
的别名:
ul_list.$set(ul_list.obj,'weight','200');
有时你可能需要为已有对象赋予多个新属性,在这种情况下,你应该用两个对象的属性创建一个新的对象。
ul_list.obj = Object.assign({},ul_list.obj,{
color:'white',
food:'meat'
})
结果为:
(6)显示过滤/排序结果
有时想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。在这种情况下,可以创建返回过滤或排序数组的计算属性。
例如:
<li v-for="n in evenNumbers">{{ n }}</li> data: { numbers: [ 1, 2, 3, 4, 5 ] }, computed: { evenNumbers: function () { return this.numbers.filter(function (number) { return number % 2 === 0 }) } }
在计算属性不适用的情况下 (例如,在嵌套 v-for
循环中) 你可以使用一个 method 方法:
<li v-for="n in even(numbers)">{{ n }}</li> data: { numbers: [ 1, 2, 3, 4, 5 ] }, methods: { even: function (numbers) { return numbers.filter(function (number) { return number % 2 === 0 }) } }
结果:
(7)一段取值范围的v-for
v-for
也可以取整数。在这种情况下,它将重复多次模板。
<li v-for="n in 10">{{n}}</li>
结果:
(8)v-for on a <template>(待验证)
类似于 v-if
,你也可以利用带有 v-for
的 <template>
渲染多个元素。比如:
<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider" role="presentation"></li> </template> </ul>
(9)v-for结合v-if
当它们处于同一节点,v-for
的优先级比 v-if
更高,这时v-if
将分别重复运行于每个 v-for
循环中。当你想为仅有的一些项渲染节点时,这种优先级的机制十分有用,如下:
<ol id="list_area"> <li v-for="(book,index) of books" v-if="book.value>1"> 父级为{{parentMessage}}-价格为{{book.value}}-下标为-{{index}} </li> </ol> var list_area = new Vue({ el:"#list_area", data:{ parentMessage:"parent", books:[ {value:1}, {value:2}, {value:3} ] } });
结果为:只将大于1的数据渲染出来
而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if
置于外层元素 (或 <template>
)上。如:
<ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left!</p>
(10)一个组件的v-for(待验证)
.