Vue源碼系列-5- Vue.set() 使用場景以及實現原理?

官方api解釋了它的使用場景

向響應式對象中添加一個屬性,並確保這個新屬性同樣是響應式的,且觸發視圖更新。它必須用於向響應式對象上添加新屬性,因爲 Vue 無法探測普通的新增屬性 (比如 this.myObject.newProperty = ‘hi’)

所以我們要知道,只有先定義在data 裏 數據才具有響應式,如果自己後添加的屬性是不具備響應式的
比如下邊這個 屬性a 具有響應式,但是b 就不會,如果你想讓這個b 具有響應式
你可以使用 Vue.set(this.model, “b”, 20 ) 這樣寫使他具有響應式,
還有就是修改數組的某個值, 你直接通過索引的這種方式修改是不可以的,
但是你如果使用數組的 增刪改查,排序,反轉 方法 是會觸發視圖更新的,因爲Vue內部重寫了這些方法,並定義了響應式

new Vue({
	data(){
 		return {
 			list: [1, 2, 3, 4]
			model:{
				a: 10
			}
		}
	}
})
mounted(){
	this.list[0] = 5;
	this.model.b = 20;
}

那麼, 接着來看看 set 方法是怎麼做的

src/core/observer/index.js

export function set (target: Array<any> | Object, key: any, val: any): any {
  if (process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
  }
  /**
  	* 這裏判斷你傳入是數組,並且key 滿足是數組的索引
	*/
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
  }
    /**
  	* 這裏判斷你傳入的是個對象,那就直接賦值操作,最後再調用
  	* defineReactive(ob.value, key, val)方法
	*/
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    )
    return val
  }
  if (!ob) {
    target[key] = val
    return val
  }
  defineReactive(ob.value, key, val)
  // 調用 notify()方法,通知更新
  ob.dep.notify()
  return val
}

src/core/shared/util.js
isValidArrayIndex()就是判斷是否滿足是索引

/**
 * Check if val is a valid array index.
 */
export function isValidArrayIndex (val: any): boolean {
  const n = parseFloat(String(val))
  return n >= 0 && Math.floor(n) === n && isFinite(val)
}

這段代碼可以看出來,調用這個方法,傳入3個參數(target, key ,val),返回值就是最後傳入的 val
這個方法內部做了3件事(原理也比較好理解):

  1. 判斷是數組的情況 並且滿足是數組的索引, 最後通過 splice()方法來處理是增加一條數據還是 修改某個索引的數據(所以你如果不想用set,直接使用這個方法也是可以修改數組的數據的,並且具有響應式)
  2. 判斷是對象的情況, 直接通過 target[key] = val 來添加屬性或者修改屬性對應的值, 接着再調用 defineReactive(ob.value, key, val),給新添加的屬性再重新定義響應式
  3. 最後調用 notify() 方法觸發更新操作
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章