Vue2原始碼解讀 - 響應式原理及簡單實現
直接進入主題了,想必大家都知道實現vue響應式核心方法就是 Object.defineProperty
,那就從它開始說
Object.defineProperty
缺點:
- 深度監聽,需要遞迴到底,一次性計算量大
- 無法監聽新增、刪除屬性(需要vue.set 和 vue.delete)
- 無法原生監聽陣列,需要特殊處理
實現響應式
function updateView () {
console.log('檢視更新')
}
// 重新定義陣列原型
const oldArrayProperty = Array.prototype
// 建立新物件原型指向 Array.prototype,在擴充套件新的方法不會影響原型
const arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
arrProto[methodName] = function () {
updateView()
oldArrayProperty[methodName].call(this, ...arguments)
}
});
// 監聽data傳入的屬性
function defineReactive(target, key, value) {
// 深度監聽 多層物件巢狀
observer(value)
// 核心api
Object.defineProperty(target, key, {
get() {
return value
},
set(newVal) {
// 設定新值也要監聽 比如{age:27}
observer(newVal)
if (newVal !== value ) {
value = newVal
updateView()
}
}
})
}
// 監聽物件屬性
function observer(target) {
if (typeof target !== 'object' || target === null) {
// 不是物件或陣列
return target
}
// 監聽陣列 把原陣列的隱式原型賦值給我們定義好的陣列物件
if (Array.isArray(target)) {
target.__proto__ = arrProto
}
// 重新定義各個屬性,加getter、setter屬性
for(let key in target) {
defineReactive(target, key, target[key])
}
}
const data = {
name: 'zk',
age: 26,
info: {
address: 'city' // 需深度監聽
},
nums: [1, 2, 3]
}
observer(data)
// data.info.address = 'beijing' // 需要深度監聽
// data.info = {address:'beijing'} // 需要深度監聽
// data.x = 666 // 新增屬性,監聽不到 需要vue.set方法
// delete data.name // 刪除屬性,監聽不到 需要vue.delete方法
data.nums.push(21)
vue2簡單的資料雙向繫結實現
<div>內容:<span id="content"></span></div>
<input id="iptName" />
const iptName = document.getElementById('iptName')
const content = document.getElementById('content')
let obj = {
name: ''
}
let newObj = JSON.parse(JSON.stringify(obj))
Object.defineProperty(obj, 'name', {
get() {
return newObj.name
},
set(val) {
if (val === newObj.name) return
newObj.name = val
observer()
}
})
function observer () {
iptName.innerText = obj.name
content.innerText = obj.name
}
iptName.oninput = function () {
obj.name = this.value
}