vue.js 最核心的功能有兩個(gè),一個(gè)是響應(yīng)式數(shù)據(jù)綁定,二是組件系統(tǒng)。
今天講講雙向數(shù)據(jù)綁定的實(shí)現(xiàn)
vue.js和avalon.js是通過Object.defineProperty實(shí)現(xiàn)雙向數(shù)據(jù)綁定的
1、簡單用法如下
var a= {}
Object.defineProperty(a,"b",{
value:123
})
console.log(a.b);//123
2、具體參數(shù)看下面代碼、其中有比較關(guān)鍵的set和get
var obj = {}; // Creates a new object 創(chuàng)造對(duì)象
Object.defineProperty(obj, "hello", {
get: function () {return sth},
set: function (val) {/* do sth */}
})
obj.hello // 可以像普通屬性一樣讀取訪問器屬性
訪問器屬性的"值"比較特殊,讀取或設(shè)置訪問器屬性的值,實(shí)際上是調(diào)用其內(nèi)部特性:get和set函數(shù)。
obj.hello // 讀取屬性,就是調(diào)用get函數(shù)并返回get函數(shù)的返回值
obj.hello = "abc" // 為屬性賦值,就是調(diào)用set函數(shù),賦值其實(shí)是傳參
好了現(xiàn)在簡單講下如何實(shí)現(xiàn)數(shù)據(jù)視圖綁定
極簡雙向綁定的實(shí)現(xiàn)一
<input type="text" id="test">
<p id="test2"></p>
<script type="text/javascript">
var obj = {};
Object.defineProperty(obj, "test", {
set: function (newVal){
document.getElementById("test2").innerHTML = newVal;
}
})
document.addEventListener('keyup', function(e) {
obj.test = e.target.value; // input輸入的值作為obj的“動(dòng)態(tài)屬性值”,賦值給p
})
</script>
此例實(shí)現(xiàn)的效果是:隨文本框輸入文字的變化,span 中會(huì)同步顯示相同的文字內(nèi)容;在js或控制臺(tái)顯式的修改 obj.hello 的值,視圖會(huì)相應(yīng)更新。這樣就實(shí)現(xiàn)了 model => view 以及 view => model 的雙向綁定。
![vue實(shí)現(xiàn)雙向綁定的原理]554AJ.png](http://upload-images.jianshu.io/upload_images/1627906-f6bd2f7554a1e143.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
極簡雙向綁定的實(shí)現(xiàn)二
HTML
<div>
<p>你好,<span id='nickName'></span></p>
<div id="introduce"></div>
</div>
<button id="btn">點(diǎn)擊</button>
JS
var userInfo = {};
Object.defineProperty(userInfo, "nickName", {
get: function(){
return document.getElementById('nickName').innerHTML;
},
set: function(nick){
document.getElementById('nickName').innerHTML = nick;
}
});
Object.defineProperty(userInfo, "introduce", {
get: function(){
return document.getElementById('introduce').innerHTML;
},
set: function(introduce){
document.getElementById('introduce').innerHTML = introduce;
}
})
document.getElementById('btn').onclick = function(){
userInfo.nickName = "xxx";
userInfo.introduce = "我是xxx,我來自杭州,..."
}


三、分解任務(wù)
上述示例僅僅是為了說明原理。我們最終要實(shí)現(xiàn)的是:


首先將該任務(wù)分成幾個(gè)子任務(wù):
1、輸入框以及文本節(jié)點(diǎn)與 data 中的數(shù)據(jù)綁定
2、輸入框內(nèi)容變化時(shí),data 中的數(shù)據(jù)同步變化。即 view => model 的變化。
3、data 中的數(shù)據(jù)變化時(shí),文本節(jié)點(diǎn)的內(nèi)容同步變化。即 model => view 的變化。
要實(shí)現(xiàn)任務(wù)一,需要對(duì) DOM 進(jìn)行編譯,這里有一個(gè)知識(shí)點(diǎn):DocumentFragment
DocumentFragment
DocumentFragment(文檔片段)可以看作節(jié)點(diǎn)容器,它可以包含多個(gè)子節(jié)點(diǎn),當(dāng)我們將它插入到 DOM 中時(shí),只有它的子節(jié)點(diǎn)會(huì)插入目標(biāo)節(jié)點(diǎn),所以把它看作一組節(jié)點(diǎn)的容器。使用 DocumentFragment 處理節(jié)點(diǎn),速度和性能遠(yuǎn)遠(yuǎn)優(yōu)于直接操作 DOM。Vue 進(jìn)行編譯時(shí),就是將掛載目標(biāo)的所有子節(jié)點(diǎn)劫持(真的是劫持,通過 append 方法,DOM 中的節(jié)點(diǎn)會(huì)被自動(dòng)刪除)到 DocumentFragment 中,經(jīng)過一番處理后,再將 DocumentFragment 整體返回插入掛載目標(biāo)。