Vue響應式實現流程
- 模板被解析成render函式,繫結資料依賴
- 設定data中資料的getter和setter特性。屬性變化,重新觸發render
Vue的模板解析
Vue的模板就是HTML加上內建的模板語法,如v-if
,v-bind
等等
- 模板最終會轉換成JS程式碼(render函式)
- 模板具有邏輯,如
v-if
和v-for
,必須通過JS實現
模板
<input id="item" v-model="content"/>
複製程式碼
解析
// 只列出了主要內容
with(this){
return _c{
'input',
{
// 屬性名
attrs:{'id':'item'},
// 指令
directives:[
{
name:'model',
rawName:'v-model',
// 對應的資料
value:(content)
}
],
// 繫結的事件
on:{
"input" function($event){
title = $event.target.value
}
}
}
}
}
複製程式碼
with(this)
指的是該Vue例項_c (createElement)
就是render函式,用來建立虛擬DOM
在模板轉化為JS程式碼的階段,如果使用了v-model
,就會在生成的虛擬DOM裡面自動監聽input
事件。如果元素的內容被修改,那麼就會觸發input
事件,該事件會修改Vue元件例項中對應的資料的值。這樣一來,頁面->資料這個方向的資料繫結就實現了。
Object.defineProperty
給屬性設定getter和setter
var obj = {};
// 設定一箇中間變數,避免遞迴
var _content = '0';
Object.defineProperty(obj,'content',{
get(){
console.log('get success')
return _content;
},
set(value){
console.log('set success')
_content = value;
}
})
// obj.content
// get success
// 0
// obj.content = 1
// get success
// 1
// obj.content
// get success
// 1
複製程式碼
所以Vue資料->頁面的資料繫結就是在屬性的setter裡面呼叫render函式。這樣每次資料更新,都會重新渲染頁面
模擬實現
<div>
<input id="item"/>
</div>
複製程式碼
var item = document.getElementById('item')
var obj = {};
var _content = 0;
Object.defineProperty(obj,'content',{
get(){
return _content;
},
set(value){
_content = value;
// 資料變化,重新渲染頁面資料
item.value = _content;
}
})
//渲染初始值
item.value = obj.content
//頁面內容變化,修改繫結資料
item.addEventListener('input',(event)=>{
obj.content = event.target.value
})
複製程式碼
注:由於Object.defineProperty
的效能問題,Vue2.x並沒有實現對陣列和物件屬性變化的檢查。Vue3.x已經使用Proxy
來進行雙向繫結,具體可參考
vue3.0 嚐鮮 -- 摒棄 Object.defineProperty,基於 Proxy 的觀察者機制探索