likes
comments
collection
share

(Vue)Vue 中封装的数组方法有哪些,其如何实现页面更新(Vue源码解析)

作者站长头像
站长
· 阅读数 9

Vue中封装的数组方法:

  1. push():

    • 描述:将一个或多个元素添加到数组的末尾。
    • 实现:通过拦截push方法,在添加元素后调用updateView方法来触发页面更新。
  2. pop():

    • 描述:移除数组的最后一个元素。
    • 实现:通过拦截pop方法,在移除元素后调用updateView方法来触发页面更新。
  3. shift():

    • 描述:移除数组的第一个元素。
    • 实现:通过拦截shift方法,在移除元素后调用updateView方法来触发页面更新。
  4. unshift():

    • 描述:将一个或多个元素添加到数组的开头。
    • 实现:通过拦截unshift方法,在添加元素后调用updateView方法来触发页面更新。
  5. splice():

    • 描述:从数组中添加或移除元素。
    • 实现:通过拦截splice方法,在添加或移除元素后调用updateView方法来触发页面更新。
  6. sort():

    • 描述:对数组元素进行排序。
    • 实现:通过拦截sort方法,在排序完成后调用updateView方法来触发页面更新。
  7. reverse():

    • 描述:颠倒数组中元素的顺序。
    • 实现:通过拦截reverse方法,在颠倒顺序后调用updateView方法来触发页面更新。

这些方法的实现都依赖于JavaScript的原型链和Object.defineProperty等技术,以便在调用这些方法时能够捕获到数据的变化,并及时更新页面。

总体而言,Vue通过劫持这些数组方法,实现了对数据的响应式处理,使得当数组发生变化时,相关的视图能够自动更新,提高了开发效率。

具体如何实现

在 Vue 中,对响应式处理利用的是 Object.defineProperty 对数据进行拦截,而这个方法并不能监听到数组内部变化,数组长度变化,数组的截取变化等,所以需要对这些操作进行 hack,让 Vue 能监听到其中的变化。

那 Vue 是如何实现让这些数组方法实现元素的实时更新的呢,下面是Vue 中对这些方法的封装:

// 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。然后,通过循环遍历数组的一些方法(例如pushpopshift等),对这些方法进行了重新定义。在重新定义的方法中,先调用原始的数组方法,然后获取数组对象的__ob__属性,即其Observer对象。接着,根据不同的数组操作,可能会获取到新增的元素,然后调用observeArray方法对新增的元素进行观察,以实现嵌套对象的响应式处理。最后,通过ob.dep.notify()通知依赖该数组的Watcher进行更新。

简单来说就是,重写了数组中的那些原生方法,首先获取到这个数组的__ob__,也就是它的 Observer 对象,如果有新的值,就调用observeArray 继续对新的值观察变化(也就是通过 target__proto__ == arrayMethods 来改变了数组实例的型),然后手动调用 notify,通知渲染 watcher,执行 update。