一:雙向數據綁定
簡介:
vue是一個mvvm框架,即數據雙向綁定,即當數據發生變化的時候,視圖也就發生變化,當視圖發生變化的時候,數據也會跟 着同步變化。
原理:
var obj = {
foo: 'foo'
}
Object.defineProperty(obj, 'foo', {
get: function () {
console.log('將要讀取obj.foo屬性');
},
set: function (newVal) {
console.log('當前值爲', newVal);
}
});
obj.foo; // 將要讀取obj.foo屬性
obj.foo = 'name'; // 當前值爲 name
// get爲我們訪問屬性時調用,set爲我們設置屬性值時調用。
-
vue.js 是採用數據劫持結合發佈者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter,getter,在數據變動時發佈消息給訂閱者,觸發相應的監聽回調。
-
第一步:需要observe的數據對象進行遞歸遍歷,包括子屬性對象的屬性,都加上 setter和getter。這樣的話,給這個對象的某個值賦值,就會觸發setter,那麼就能監聽到了數據變化;
-
第二步:compile解析模板指令,將模板中的變量替換成數據,然後初始化渲染頁面視圖,並將每個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變動,收到通知,更新視圖;
-
第三步:Watcher訂閱者是Observer和Compile之間通信的橋樑,主要做的事情是:
-
1、在自身實例化時往屬性訂閱器(dep)裏面添加自己
-
2、自身必須有一個update()方法
-
3、待屬性變動dep.notice()通知時,能調用自身的update()方法,並觸發Compile中綁定的回調,則功成身退。
-
-
第四步:MVVM作爲數據綁定的入口,整合Observer、Compile和Watcher三者,通過Observer來監聽自己的model數據變化,通過Compile來解析編譯模板指令,最終利用Watcher搭起Observer和Compile之間的通信橋樑,達到數據變化 -> 視圖更新;視圖交互變化(input) -> 數據model變更的雙向綁定效果。
二:diff流程
1:當數據發生變化時,vue是怎麼更新節點的?
我們先根據真實DOM生成一顆virtual DOM
,當virtual DOM
某個節點的數據改變後會生成一個新的Vnode
,然後Vnode
和oldVnode
作對比,發現有不一樣的地方就直接修改在真實的DOM上,然後使oldVnode
的值爲Vnode
。
diff的過程就是調用名爲patch
的函數,比較新舊節點,一邊比較一邊給真實的DOM打補丁,更新相應的視圖。
2. virtual DOM和真實DOM的區別?
virtual DOM是將真實的DOM的數據抽取出來,以對象的形式模擬樹形結構。比如dom是這樣的:
<div> <p>123</p> </div>
對應的virtual DOM(僞代碼):
var Vnode = { tag: 'div', children: [ { tag: 'p', text: '123' } ] };
VNode
和oldVNode
都是對象
3:diff流程
- patch函數接收兩個參數oldVnode和Vnode分別代表新的節點和之前的舊節點
- 判斷兩節點是否值得比較,值得比較則執行patchVnode;
- 不值得比較則用Vnode替換oldVnode;
- patchVnode:當我們確定兩個節點值得比較之後我們會對兩個節點指定patchVnode方法;
- 找到對應的真實dom,稱爲el;
- 判斷Vnode和oldVnode是否指向同一個對象,如果是,那麼直接return;
- 如果他們都有文本節點並且不相等,那麼將el的文本節點設置爲Vnode的文本節點;
- 如果oldVnode有子節點而Vnode沒有,則刪除el的子節點;
- 如果oldVnode沒有子節點而Vnode有,則將Vnode的子節點真實化之後添加到el;
- 如果兩者都有子節點,則執行updateChildren函數比較子節點,這一步很重要;
- updateChildren函數圖解
現在分別對oldS、oldE、S、E兩兩做sameVnode比較,有四種比較方式,當其中兩個能匹配上那麼真實dom中的相應節點會移到Vnode相應的位置,這句話有點繞,打個比方:
- 如果是oldS和E匹配上了,那麼真實dom中的第一個節點會移到最後;
- 如果是oldE和S匹配上了,那麼真實dom中的最後一個節點會移到最前,匹配上的兩個指針向中間移動;
- 如果四種匹配沒有一對是成功的,那麼遍歷oldChild,S挨個和他們匹配,匹配成功就在真實dom中將成功的節點移到最前面,如果依舊沒有成功的,那麼將S對應的節點插入到dom中對應的oldS位置,oldS和S指針向中間移動。