Vue中封装的数组方法:
-
push():
- 描述:将一个或多个元素添加到数组的末尾。
- 实现:通过拦截
push
方法,在添加元素后调用updateView
方法来触发页面更新。
-
pop():
- 描述:移除数组的最后一个元素。
- 实现:通过拦截
pop
方法,在移除元素后调用updateView
方法来触发页面更新。
-
shift():
- 描述:移除数组的第一个元素。
- 实现:通过拦截
shift
方法,在移除元素后调用updateView
方法来触发页面更新。
-
unshift():
- 描述:将一个或多个元素添加到数组的开头。
- 实现:通过拦截
unshift
方法,在添加元素后调用updateView
方法来触发页面更新。
-
splice():
- 描述:从数组中添加或移除元素。
- 实现:通过拦截
splice
方法,在添加或移除元素后调用updateView
方法来触发页面更新。
-
sort():
- 描述:对数组元素进行排序。
- 实现:通过拦截
sort
方法,在排序完成后调用updateView
方法来触发页面更新。
-
reverse():
- 描述:颠倒数组中元素的顺序。
- 实现:通过拦截
reverse
方法,在颠倒顺序后调用updateView
方法来触发页面更新。
这些方法的实现都依赖于JavaScript的原型链和Object.defineProperty
等技术,以便在调用这些方法时能够捕获到数据的变化,并及时更新页面。
总体而言,Vue通过劫持这些数组方法,实现了对数据的响应式处理,使得当数组发生变化时,相关的视图能够自动更新,提高了开发效率。
具体如何实现
在 Vue 中,对响应式处理利用的是 Object.defineProperty 对数据进行拦截,而这个方法并不能监听到数组内部变化,数组长度变化,数组的截取变化等,所以需要对这些操作进行 hack,让 Vue 能监听到其中的变化。
那 Vue 是如何实现让这些数组方法实现元素的实时更新的呢,下面是Vue 中对这些方法的封装:
js
// src/core/observer/array.js
// 引入 def 函数,用于在对象上定义属性
import { def } from '../util/index';
// 获取数组的原型对象
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 函数定义属性
def(arrayMethods, method, function mutator (...args) {
// 调用原始的数组方法,获取原始方法的返回值
const result = original.apply(this, args);
// 获取数组对象的 Observer 对象
const ob = this.__ob__;
// 对于一些方法(push、unshift、splice)可能会新增元素,需要进行响应式处理
let inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
// splice 方法的参数从第三个参数开始是新增的元素
inserted = args.slice(2);
break;
}
// 如果有新增的元素,调用 Observer 对象的 observeArray 方法进行观察
if (inserted) ob.observeArray(inserted);
// 通知变化
ob.dep.notify();
// 返回原始方法的返回值
return result;
});
});
上述代码首先创建了一个名为
arrayMethods
的对象,该对象继承自数组的原型对象Array.prototype
。然后,通过循环遍历数组的一些方法(例如push
,pop
,shift
等),对这些方法进行了重新定义。在重新定义的方法中,先调用原始的数组方法,然后获取数组对象的__ob__
属性,即其Observer对象。接着,根据不同的数组操作,可能会获取到新增的元素,然后调用observeArray
方法对新增的元素进行观察,以实现嵌套对象的响应式处理。最后,通过ob.dep.notify()
通知依赖该数组的Watcher进行更新。
简单来说就是,重写了数组中的那些原生方法,首先获取到这个数组的__ob__,也就是它的 Observer 对象,如果有新的值,就调用observeArray 继续对新的值观察变化(也就是通过 target__proto__ == arrayMethods
来改变了数组实例的型),然后手动调用 notify,通知渲染 watcher,执行 update。