<template> <ul> <li v-for="item in items" :key="item.id"> {{ item }} </li> </ul> </template> <script> export default { data() { return { items: ['Apple', 'Banana', 'Orange'] } } } </script>
在上面的例子中,我们使用v-for指令来渲染一个名为items的数组。这个数组包含了三个元素:Apple、Banana和Orange。Vue会自动跟踪每个元素的编号,并在每次数组发生变化时更新DOM。
this.items.push('Pear');
上面的代码会向数组items中添加一个新元素'Pear'。push方法的返回值是新数组的长度。当使用Vue的push方法时,它会自动触发DOM更新。这意味着我们不需要手动调用set或者set或者set或者forceUpdate方法来更新DOM。Vue的push方法会在更新DOM前调用数组的原生push方法,并传递相同的参数。当数组发生变化时,Vue会检测到变化并更新DOM。
需要注意的是,如果我们向数组中添加对象类型的元素,那么这个对象必须是响应式的。否则,这个对象的属性变化时不会触发DOM更新。为了避免这种情况,我们应该在添加对象前先使用Vue.set或者this.$set方法将其转换为响应式对象。
let lastItem = this.items.pop();
上面的代码会从数组items中删除最后一个元素,并返回该元素。当使用Vue的pop方法时,它会自动触发DOM更新,无需手动调用set或者set或者set或者forceUpdate方法。
let firstItem = this.items.shift();
上面的代码会从数组items中删除第一个元素,并返回该元素。当使用Vue的shift方法时,它会自动触发DOM更新,无需手动调用set或者set或者set或者forceUpdate方法。
this.items.unshift('Grape');
上面的代码会向数组items头部添加一个新元素'Grape'。当使用Vue的unshift方法时,它会自动触发DOM更新,无需手动调用set或者set或者set或者forceUpdate方法。
this.items.splice(1, 0, 'Kiwi', 'Mango');
上面的代码会从数组items中的索引1位置开始,删除0个元素,并添加两个新元素Kiwi和Mango。当使用Vue的splice方法时,它会自动触发DOM更新,无需手动调用set或者set或者set或者forceUpdate方法。
需要注意的是,如果我们向数组中添加对象类型的元素,那么这个对象必须是响应式的。否则,这个对象的属性变化时不会触发DOM更新。为了避免这种情况,我们应该在添加对象前先使用Vue.set或者this.$set方法将其转换为响应式对象。
this.items.sort();
上面的代码会按照字母顺序对数组items进行排序。当使用Vue的sort方法时,它会自动触发DOM更新,无需手动调用set或者set或者set或者forceUpdate方法。需要注意的是,如果我们想要按照自定义的规则对数组进行排序,那么我们需要传递一个比较函数作为sort方法的参数。而这个比较函数不能直接修改数组,否则Vue无法监测到变化。如果需要修改数组,我们应该使用变异方法。
this.items.reverse();上面的代码会逆序排列数组items中的元素。当使用Vue的reverse方法时,它会自动触发DOM更新,无需手动调用set或者set或者set或者forceUpdate方法。
源码分析
Vue的数组变异方法是在响应式系统中实现的:// 堆代码 duidaima.com export function def(obj: Object, key: string, val: any, enumerable?: boolean) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true }); }
其中,obj表示要定义属性或方法的对象,key表示要定义的属性或方法的名称,val表示要定义的属性或方法的值,enumerable表示该属性或方法是否可枚举。该函数使用Object.defineProperty方法将属性或方法转换为getter和setter。
// 定义数组变异方法 const arrayProto = Array.prototype; export const arrayMethods = Object.create(arrayProto); ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) { // 缓存原生方法 const original = arrayProto[method]; // 调用数组变异方法时会触发依赖更新 def(arrayMethods, method, function mutator(...args) { const result = original.apply(this, args); const ob = this.__ob__; let inserted; switch (method) { case 'push': case 'unshift': inserted = args; break; case 'splice': inserted = args.slice(2); break; } // 对新插入的元素进行响应式处理 if (inserted) ob.observeArray(inserted); ob.dep.notify(); return result; }); });在上述源码中,我们首先使用Object.create方法创建了一个以Array.prototype为原型的新对象arrayMethods,然后遍历数组变异方法并缓存原生方法。