vue深度學習之對象和數組的監聽原理


對象的監聽

定義一個defineReactive對Object.defineProperty進行封裝,用於監聽對象的調用和改變

// 傳入的key要是計算屬性格式喲
function defineReactive(obj, key, val) {
 const dep = new Dep() // 用於收集依賴的類,具體原理就不講解了
 Object.defineProperty(obj, key) {
  enumerable: true,
  configurable: true,
  get: function () { // 每次調用到對象屬性時都會出發這個函數
    dep.depend()// window.target是一個方法,用於存放調用的實例的依賴
    return val // 返回調用對象時獲取到的值
  },
  set: function (newVal) {
   if (val === newVal) return
   for (i = 0; i < dep.length; i ++) {
    dep.notify()  // 更顯依賴綁定的數據值,實現頁面的重新渲染
   }
   val = newVal
  }
 }
}

數組的監聽

數組的監聽和對象略有不同。除了通過賦值操作改變數組外,還可以通過數組的一些自帶方法改變數組,而通過后面那種方式該改變數組setter是監聽不到的。

解決方法:攔截數組的原型,並賦予新的原型。

// 通過整理,數組中能夠改變數組本身的有七個方法

// 數組能夠改變自身的方法:
  const arrayProto = Array.prototype
  // Object.create(obj) 創建一個新對象,並且使用現有對象obj作為新對象的__proto__
  const arrayMethods = Object.create(arrayProto);
  
  [
    'push',  // 在數組最后追加一個元素
    'pop',  // 刪除最后一個元素
    'unshift', // 在第一個元素位添加一個元素
    'shift',  // 刪除第一個元素
    'splice', // 添加或者刪除元素
    'sort',  // 排序
    'reverse' // 反向排序
  ].forEach( method => {
    // 緩存原始方法
    var original = arrayProto[method]
 
    Object.defineProperty(arrayMethods, method, {
      value: function mutator (...args) {  // args是調用原型方法時傳入的參數
        console.log('this', this) // this指向屬性所屬對象
        ... // 在這里面執行監聽變化的操作
        return original.apply(this, args)
      }
    })
  })
  module.exports = arrayMethods  

只對需要覆蓋原型的數組實例進行原型攔截;避免直接改變Array.proptotype造成的全局污染

const  arrayMethods = require('./3-2攔截器')
// 讓攔截器只覆蓋那些響應式數組的原型,而非構造函數Array的原型

// 考慮到有些瀏覽器不支持原型的情況,要進行判斷
const hasProto = '__ptoto__' in {} // 有酒返回true,沒有就返回false

export class Observe {
  // 數組實例; value的實例數組
  constructor (value) {
    this.value = value
    if (Array.isArray(value)) {
      // 添加攔截器
      const augment = hasProto ? protoAugment : copyAugment
      augment(value, arrayMethods, arrayKeys)
          
      } else {
       // 如果瀏覽器不支持原型,就直接將方法添加到數組上
      }
     
    } else {
      this.walk(value) // 如果數據是其他類型時執行
    }
  }
}

// 用攔截器直接覆蓋原型
function protoAugment (value, arrayMethods, arrayKeys) {
  value.__proto__ = arrayMethods  
}

// 將添加了攔截器的方法追加到數組的屬性中
function copyAugment (value, arrayMethods, keys) {
  for(i = 0; i < keys.length; i ++) {
     const key = keys[i]
    def(value, key, src[key])
  }
}

收集依賴:將數組作為對象的屬性傳入defineReactive方法

function defineReactive (data, key, val) {
  if (typeof val === 'object') new Observer()
  let dep = new dep()
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function () {
      dep.depend() // 收集array的依賴
      return value
    },
    set: function (newVal) {
      if (val === newVal) return
      dep.notify() // 監聽array的變化
      val = newVal
    }
  })
}

缺陷:像通過直接改變數組項的值(比如:arr[2]=23)或者通過arr.length改變數組的長度是不能被監聽到的。通過es6的元編程能力可以解決,后續再了解。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM