本文會帶大家手動實現乙個雙向繫結過程(僅僅涵蓋一些簡單的指令解析,如:v-text
,v-model
,插值),當然借鑑的是vue1的原始碼,相信大家在閱讀完本文後對vue1會有乙個更好的理解,源**放到了github,由於本人水平有限,理解不到位的地方還請大家指出。
mvvm
使開發可以更加關注於資料,減少了很大的工作量,也使**可讀性,可維護性更高,mvvm
核心的思想就是檢視是狀態的函式:view = viewmodel(model),所以當model發生改變時,viewmodel會來操作view來怎麼做,而非是自己寫**來做。無論是雙向繫結還是單向繫結,都是符合mvvm
思想的。vue提倡的是雙向繫結,也就是允許view到model的變化,其實這個場景出現在的也就是表單操作上,看個例子,例子中分別利用了vue和react實現了一下表單value
變化,影響頁面與其相關的dom
節點發生變化,可以發現的是雙向繫結的vue是input
的value
發生變化則h1
的innertext
就發生了變化,變化是由view->model,而提倡單向資料流的react
需要手動監聽事件,事件觸發後,更改model的值,從而使input
的value
發生了變化。看了vue的原始碼後不難發現vue的雙向繫結的實現也就是在表單元素上新增了input
事件,可以說雙向繫結是單向繫結的乙個語法糖。
上圖是乙個大體的流程,下面按照流程來實現下:
-利用observer
對data
進行了監聽,並且提供訂閱某個資料項的變化的能力
這點的實現,需要借助的是object.defineproperty()
來為物件的屬性繫結get/set
特性(由於利用了object.defineproperty()
,所以vue不支援ie8),observer
需要將data
的所有屬性都繫結get/set
,很容易想到的就是利用遞迴來實現,具體**就不貼出,請參見這裡。
-利用compile
對模板進行解析
這點實現的是將我們的模板轉化為html
,過程中會將資料與view中的節點相關聯起來,最終會將編譯好的html
頁面替換到頁面上。首先來看解析,首先從根節點開始,根據不同的節點型別採用不同的解析方式:
function compilenode(node, vm) else
if (type === 3 && node.data.trim()) else
}
對於文字節點來說,可能存在情況只有兩種:
與資料不相關不用操作
含有插值,需要與資料進行關聯
利用下面正就可以將插值找出:
/\
\}\}|\
\}/g
採用下面函式來對文字節點的內容解析:
function parsetext(node)
const tokens = ;
var lastindex = tagre.lastindex = 0,
match, index, html, value;
while (match = tagre.exec(text)) )
}html = htmlre.test(match[0]);
value = html ? match[1] : match[2];
tokens.push();
lastindex = index + match[0].length;
}if (lastindex < text.length) )
}return tokens;
}
返回了tokens
,裡面儲存了每乙個塊內容,乙個插值or乙個普通文字,tag
來標記是否為插值,html
來標記是否為純html
插值。遍歷返回的tokens
,根據不同的型別,來採用不同的方式將其新增到其父節點上:
function
compiletextnode
(node, vm) else
} else
});return replace(node, frag);
}
dircollection
是乙個指令集合,也就是決定了如何初始化以及如何更新該節點。對於nodetype
為1
的節點來說,指令全部儲存在其屬性中,遍歷屬性,假若指令中含有v-html,v-model,v-text
,則停止遍歷其子樹,直接將呼叫相應指令即可,否則,則需要遍歷其子節點,對其子節點應用compilenode
進行解析:
function compilenodelist(nodes, vm)
}function compileelement(node, vm) else
// 指令中為v-html or v-text or v-model終止遞迴
flag = flag ||
name === vhtml ||
name === vtext;
node.removeattribute(name);
}
});const
childs = node.childnodes;
if(!flag && childs && childs.length)
}
在dircollections
中還會做的就是將資料與view的dom
節點相關聯,利用的就是dep
與watcher
,頁面上每乙個與資料相關聯的節點都含有乙個watcher
,當資料發生變化是watcher
用於計算,是否需要更新該節點;資料的每乙個屬性都有乙個dep
,當該屬性發生變化時,dep
會通知與該資料相關聯的watcher
來進行計算是否需要更新對應頁面。dep**,watcher**。
-非同步更新佇列
vm.value++;
vm.value++;
vm.value++;
三次資料改變,假若同步更新的話,則每次資料改變會立即更新dom
,而非同步更新的話,可以先將更新推入乙個佇列中,由於是非同步,也可以保證每乙個watcher
只被推入到一次,這樣就避免了不必要的更新,非同步更新主要利用的是nexttick
,這個函式會優先使用promise
,不相容則利用mutationobserver
,再不相容的話會利用settimeout
。
看過了vue的原始碼不得不感嘆vue的優美,而vue2又增加了虛擬dom,這樣就可以做到服務端渲染,給了我們更多的可能!
這篇部落格最好配合著原始碼來看,關於原始碼歡迎star
自己實現MVVM(Vue原始碼解析)
本文會帶大家手動實現乙個雙向繫結過程 僅僅涵蓋一些簡單的指令解析,如 v text,v model,插值 當然借鑑的是vue1的原始碼,相信大家在閱讀完本文後對vue1會有乙個更好的理解,源 放到了github,由於本人水平有限,理解不到位的地方還請大家指出。mvvm使開發可以更加關注於資料,減少了...
azkaban web server原始碼解析
azkaban主要用於hadoop相關job任務的排程,但也可以應用任何需要排程管理的任務,可以完全代替crontab。azkaban主要分為web server 任務上傳,管理,排程 executor server 接受web server的排程指令,進行任務執行 1.資料表 projects 工...
JDK LinkedHashMap原始碼解析
今天來分析一下jdk linkedhashmap的源 public class linkedhashmapextends hashmapimplements map可以看到,linkedhashmap繼承自hashmap,並且也實現了map介面,所以linkedhashmap沿用了hashmap的大...