MST

星途 面试题库

面试题:Vue响应式系统中数组变异方法的特殊处理机制

Vue对数组的变异方法(如push、pop等)做了特殊处理以实现响应式。请深入分析Vue是如何做到对这些数组方法进行拦截,使得数组变化时能够通知视图更新的?如果自定义一个数组的方法,要如何让Vue的响应式系统感知到这个新方法带来的数组变化?
22.4万 热度难度
前端开发Vue

知识考点

AI 面试

面试题答案

一键面试

Vue对数组变异方法的拦截实现响应式原理

  1. 重写数组原型方法
    • Vue在初始化时,通过Object.create创建了一个新的对象,它的__proto__指向原生数组的原型Array.prototype
    • 然后在这个新对象上重新定义了数组的变异方法,如pushpopshiftunshiftsplicesortreverse。例如:
    const arrayProto = Array.prototype
    export const arrayMethods = Object.create(arrayProto)
    const methodsToPatch = [
      'push',
      'pop',
    'shift',
      'unshift',
    'splice',
    'sort',
    'reverse'
    ]
    methodsToPatch.forEach(function (method) {
      const original = arrayProto[method]
      Object.defineProperty(arrayMethods, method, {
        value: 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
        },
        enumerable: false,
        writable: true,
        configurable: true
      })
    })
    
    • 当Vue实例中的数组被创建时,会将该数组的__proto__指向arrayMethods。这样,当调用数组的变异方法时,实际调用的是arrayMethods上重写的方法。
  2. 依赖收集与通知
    • 在重写的变异方法中,首先调用原生数组的对应方法(如original.apply(this, args))来执行真正的数组操作。
    • 然后,对于pushunshiftsplice这些可能会插入新元素的方法,会对新插入的元素进行响应式处理(通过ob.observeArray(inserted))。这里的ob是数组的观察者实例,observeArray方法会递归地为新插入的数组元素创建观察者。
    • 最后,调用ob.dep.notify()来通知依赖该数组的所有订阅者(即视图相关的Watcher),让它们知道数组发生了变化,从而触发视图更新。

自定义数组方法让Vue响应式系统感知变化

  1. 手动触发依赖通知
    • 如果自定义一个数组方法,假设为customMethod,在方法内部,首先要调用原生数组的相关操作来改变数组。例如,如果是类似push的功能,可以这样写:
    Array.prototype.customMethod = function (newItem) {
      const originalPush = Array.prototype.push
      originalPush.call(this, newItem)
      const ob = this.__ob__
      if (ob) {
        ob.dep.notify()
      }
    }
    
    • 这里通过获取数组的观察者实例ob,然后调用ob.dep.notify()来手动触发依赖通知,这样Vue的响应式系统就能感知到数组变化并更新视图。
  2. 使用Vue.set方法
    • 另一种方式是利用Vue提供的Vue.set方法(在组件内可以直接用this.$set)。例如:
    Array.prototype.customMethod = function (newItem) {
      const originalPush = Array.prototype.push
      originalPush.call(this, newItem)
      this.$set(this, this.length - 1, newItem)
    }
    
    • Vue.set方法不仅会触发响应式更新,还会处理一些边界情况,比如在对象中添加新属性时确保其响应式等。在数组场景下,它能保证新添加的元素也被正确地设置为响应式,并且通知视图更新。