深入淺出vuejs讀書筆記----------2、Array的變化偵測

1、如何追蹤變化

在這裏插入圖片描述
如上圖,使用攔截器將array.prototype覆蓋。當使用array原型中的方法時,其實使用的是攔截器中的方法。通過攔截器,就可以偵聽Array的變化。

2、攔截器

攔截器:和array.prototype一樣的object,裏面的屬性一樣,只不過可改變數組自身的方法是處理過的。
array原型中可改變自身的方法有7個:push, pop, shift, unshift, splice, sort 和 reverse 。

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)  // 創建新的對象
['push','pop','shift','unshift','splice','sort','reverse'].foreach(function(method){
  const original = arrayProto[method]  // 緩存原始方法
  Object.defineProperty(arrayMethods, method, {
    value: function mutator (...args) {
			return original.apply(this, args)
    },
    enumerable:false,
    writable:true,
    configurable:true
  })
})

Object.create
Object.create(proto,[propertiesObject])
proto: 新創建對象的原型對象
[propertiesObject]:可選,添加到新對象的屬性,不屬於其原型鏈的屬性
詳情可參考:https://juejin.im/post/5acd8ced6fb9a028d444ee4e

Object.defineProperty
直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。
Object.defineProperty(obj, prop, descriptor)
obj: 要在其上定義屬性的對象。
prop: 要定義或修改的屬性的名稱。
descriptor: 將被定義或修改的屬性描述符。
詳情可參考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

通過Observer將數據轉換成響應式的,在Observer中覆蓋會被轉換成響應式Array的原型。

export class Observer {
	constructor (value) {
  	this.value = value
    
    if (Array.isArray(value)) {
    	value.__prop__ = arrayMethods  // 新增
    }else {
    	this.walk(value)
    }
  }
}

value.prop = arrayMethods :就是將攔截器賦值給value.prop,通過__prop__覆蓋value的原型。如下圖所示。
在這裏插入圖片描述
以上是瀏覽器支持__prop__的情況。當瀏覽器不支持__prop__該怎麼辦呢?
vue的做法:如不可以使用__prop__,直接將arrayMethods的方法設置到被偵測的數組上。

const hasProto = '__ prop__' in {}
const arrayKeys = Object.getOwnPropertyNames(arrayMethods)

export class Observer {
  constructor(value){
  	this.value = value
    
    if(Array.isArray(value)) {
    	const augment = hasProto ? protoAugment : copyAugment
      augment(value,arrayMethods,arrayKeys)
    }else {
    	this.walk(value)
    }
  }
  ...
}
  function protoAugment(target,src,keys){
  	target.__prop__ = src
  }
	function copyAugment(target,src,keys){
  	for(let i = 0;i < keys.length;i++){
    		const key = keys[i]
        def(target,key,src[key])
    	}
  }

​Object.getOwnPropertyNames()
返回一個由指定對象的所有自身屬性的屬性名組成的數組。

var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]

// 類數組對象
var obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"]

// 使用Array.forEach輸出屬性名和屬性值
Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) {
  console.log(val + " -> " + obj[val]);
});
// 輸出
// 0 -> a
// 1 -> b
// 2 -> c

Array和object一樣,都是在getter中收集依賴,並將依賴存到def中。

Array在getter中收集依賴,在攔截器中觸發依賴

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章