MST

星途 面试题库

面试题:Vue响应式原理在数组和对象上的差异及实现方式

在Vue的响应式系统中,对象和数组的响应式实现有哪些不同?Vue是如何对数组的变异方法进行拦截以实现响应式的?
41.6万 热度难度
前端开发Vue

知识考点

AI 面试

面试题答案

一键面试

对象和数组响应式实现的不同

  1. 对象:Vue通过Object.defineProperty()方法来进行响应式的实现。遍历对象的属性,为每个属性定义gettersetter。当访问属性时,getter会被触发用于依赖收集;当属性值改变时,setter会被触发从而通知相关依赖进行更新。
  2. 数组:由于JavaScript中无法拦截对数组索引的直接赋值以及length属性的改变,Vue对数组不能像对象那样简单地使用Object.defineProperty()。Vue采用覆盖数组原型的方式,对数组的变异方法(如pushpopshiftunshiftsplicesortreverse)进行拦截,在这些方法被调用时触发视图更新。同时,Vue还提供了$set方法来对数组的指定索引进行响应式更新。

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

  1. 原型覆盖:Vue创建了一个新的数组原型arrayMethods,它继承自原生的Array.prototype。在arrayMethods上重新定义了数组的变异方法。
  2. 方法拦截:当Vue实例中的数组调用这些变异方法时,实际调用的是arrayMethods上重新定义的方法。在这些重新定义的方法内部,首先调用原生的数组方法,保证数组行为的正确性。然后,通过发布 - 订阅模式通知依赖(如视图),告知数组发生了变化,从而触发视图更新。例如,对于push方法的实现:
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
  })
})

在上述代码中,mutator函数在调用原生push等方法后,会对新插入的元素进行响应式处理(如果有新元素),并且通过ob.dep.notify()通知依赖更新。