引言
看过多遍 vue.js 文档,但是对一些知识点始终似懂非懂。于是便有了重读 Vue.js 文档的计划。此篇对 Vue 中数组更新进行剖析并结合源码,希望对您有所帮助。
变异方法(mutation method)
顾名思义,变异方法,即会改变调用了这些方法的原始数组。但由于数组是引用类型,存于栈内存中的地址是不会改变的,改变的是真实的数组值。包括:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
const arr = arr1 = [1, 2, 3];
arr === arr1; // true arr === arr1 == [1, 2, 3];
arr.push(4);
arr === arr1; // true arr === arr1 == [1, 2, 3, 4]; 数组值改变了,但两数组依然指向同一地址
非变异方法(non-mutation method)
即不会改变调用这些方法的数组,而总是返回一个新数组。例如:concat、filter、map、slice 等等。但我们使用非变异方法,可以用新数组替换旧数组。
new Vue({
data () {
return {
imgs: ['https://juejin.im/1', 'https://juejin.im/2', 'https://juejin.im/3', 'https://juejin.im/4', 'https://juejin.im/5']
}
},
methods: {
transform () {
this.imgs = this.imgs.map(img => img + '.jpg'); // 用新数组替换旧数组
}
}
})
数组更新
无论是变异数组或是非变异数组,当数组值改变的时候,地址指针都不会改变。而 vue 是一个以数据驱动的框架,当数组值变化的时候,自然希望依赖于变化数据的视图也触发更新。用过 vue 的小伙伴都知道,当数组改变的时候,视图也更新了。那 vue 帮我们做了什么事情呢,就让我们深入源码一探究竟吧。
// vue 2.6
// src/core/observer/array.js
const arrayProto = Array.prototype; // 获取数组原型对象
const arrayMethods = Object.create(arrayProto); // 生成一个新对象,且__proto__ 指向 数组原型对象
const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
/**
* Intercept mutating methods and emit events
*/
methods.forEach(method => {
const original = arrayProto[method];
let val = function mutator (...args) {
const result = original.apply(this, args); //缓存原始方法
let inserted = null;
switch (method) {
case 'push':
inserted = args;
break;
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.slice(2); // array.splice(start[, deleteCount[, item1[, item2[, ...]]]]) [item])
break;
}
if (inserted) observeArray(inserted); // 获取插入的值,并设置响应式监听
notify(); // 通知依赖像更新
return result;
}
Object.defineProperty(arrayMethods, method, {
value: val,
writable: true,
configurable: true,
enumerable: true
})
})
const observeArray = function observeArray (inserted) { // 简写,[具体实现](https://github.com/vuejs/vue/blob/2.6/src/core/observer/index.js)
console.log(inserted); //新增的元素形成的数组
console.log('响应式监听数组');
}
const notify = function notify () {
console.log('视图更新了');
}
注意事项
由于 JavaScript 的限制,Vue 不能检测以下数组的变动:
- 利用索引直接设置一个数组项,例:
vm.imgs[index] = newValue
- 修改数组的长度, 例:
vm.imgs.length = newLength
为解决第一类问题:
// Vue.set
Vue.set(vm.imgs, index, newValue);
// Array.prototype.splice
vm.imgs.splice(index, 1, newValue);
为解决第二类问题:
vm.imgs.splice(newLength);
总结
以上是个人对vue 数组的一些理解,希望能对大家有所帮助。如果有错误或不严谨的地方,欢迎批评指正,如果喜欢,欢迎点赞。