一、vue雙向數據綁定原理
vue實現雙向數據綁定是通過Object.defineProperty()方法來實現劫持的
Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象
Object.defineProperty()方法解析
Object.defineProperty()方法有三個參數:
Object.defineProperty(obj, key, options)
參數 | 功能/作用 |
---|---|
obj | 要修改或定義key值的對象 |
key | 對應obj對象的里面某有已有或要修改的屬性 |
options | 這個參數對象里面有get,set兩個函數,用來定義屬性值 |
options有兩種主要形式:數據描述符和存取描述符
數據描述符是一個具有值的屬性,該值可能是可寫的,也可能不是可寫的
存取描述符是由getter-setter函數對描述的屬性
描述符必須是這兩種形式之一;不能同時是兩者
options參數配置:
數據描述符和存取描述符均具有以下可選鍵值
{
configurable: Boolean, // 當且僅當該屬性的 configurable 為 true 時,該屬性描述符才能夠被改變,同時該屬性也能從對應的對象上被刪除,默認為 false
enumerable: Boolean, // 當且僅當該屬性的enumerable為true時,該屬性才能夠出現在對象的枚舉屬性中
}
數據描述符同時具有以下可選鍵值
{
value: undefined, // 該屬性對應的值。可以是任何有效的 JavaScript 值(數值,對象,函數等)。默認為 undefined
writable: Boolean, // 當且僅當該屬性的writable為true時,value才能被賦值運算符改變。默認為 false
}
存取描述符同時具有以下可選鍵值:默認值:undefined
{
get: fn, // 一個給屬性提供 getter 的方法,如果沒有 getter 則為 undefined。當訪問該屬性時,該方法會被執行,方法執行時沒有參數傳入,但是會傳入this對象(由於繼承關系,這里的this並不一定是定義該屬性的對象),這個方法沒有參數
set: fn, // 一個給屬性提供 setter 的方法,如果沒有 setter 則為 undefined。當屬性值修改時,觸發執行該方法。該方法有一個參數,就是對應的每次修改的新值
}
二、模擬測試
let Test = {
name: 1
}
console.log(Test.name) // 1
如果我現在想要讓1,輸出true,0輸出false,怎么辦?我們可以通過Object.defineProperty()方法實現
let Test = {}
let name = ''
Object.defineProperty(Test, 'name', {
configurable: true,
enumerable: true,
set: function (val) {
name = val
console.log('你輸入的值為:' + val)
},
get: function () {
if (name === 1) {
name = true
console.log('獲取值的時候觸發!')
return name
} else if (name === 0) {
name = false
console.log('獲取值的時候觸發!')
return name
}
}
})
分別測試一下結果:
Test.name = 0 => // 當我們改變對象或者為對象添加一個屬性時,觸發了set函數:你輸入的值為:0
console.log(Test.name) => // false
console.log(name) => // false
// 通過測試改變Test對象的屬性,我們發現不僅Test對象里面改變了,而且name變量也改變了
name = 0 => // 我們發現,我們改變了name變量的值,但是並沒有觸發set函數,(可以理解為set只能觸發對象參數里面的改變)
console.log(Test.name) => // false
console.log(name) => // false
// 通過測試改變了name變量的值,發現兵沒有觸發set函數,但是Test對象和name變量的值還是跟着改變了,只觸發了get函數,相當於獲取name值
PS:這里是補充的,上面的測試依舊可以試下,但是原理和結論並不是那樣的,其實Object.defineProperty()方法可以理解為監聽的就是Test這個對象的,name就是添加的或對象已有的一個屬性,這就是為什么上面單獨改變name屬性,並沒有觸發set函數,當我們改變Test對象的name屬性時,set獲取新值,並將其重新賦值給name變量,並在get函數里返回,這樣就可以實現動態改變Test的name屬性值來保持Test.name===name,所以我們只能操作Test.name,如果要操作name,還是要賦值給Test.name,間接操作Texst.name,然后觸發后續事件
這里結合vue的雙向綁定只能在input,checkbox這些標簽上使用,可以理解為這些標簽的value的屬性值就相當於我們測試的name屬性,通過value可以時刻動態監聽input等標簽value值的變化,然后將這個新value值賦值給對象指定屬性,然后就可以觸發后續函數。
三、簡單粗暴的雙向綁定
<div id="demo"></div> // 這個是用來動態顯示input標簽里面的value值的
<input type="text" id="inp" /> // 通過監聽input標簽的value值,賦值給我們監聽的對象屬性
let Obj = {} => // 監聽對象
let demo = document.getElementById('demo') // 獲取DOM
let inp = document.getElementById('inp') // 獲取DOM
Object.defineProperty(Obj, 'text', {
configurable: true,
enumerable: true,
set: function (val) {
// 將Obj對象新改變的值賦值給input的value,然后頁賦值給demo
inp.value = val
demo.innerHTML = val
console.log('input里面輸入了' + val)
},
get: function () {
// 返回對象
return Obj
}
})
測試結果:
無論是在input標簽里面輸入,還是改變Obj.text的值,兩者都會同時改變
至此,vue雙向數據綁定原理挖坑完畢。