Vue
程序結構
Vue.js 是一個非常典型的 MVVM 的程序結構,整個程序從最上層大概分爲
全局設計:包括全局接口、默認選項等
vm 實例設計:包括接口設計 (vm 原型)、實例初始化過程設計 (vm 構造函數)
這裏面大部分內容可以直接跟 Vue.js 的官方 API 參考文檔對應起來,但文檔裏面沒有且值得一提的是構造函數的設計,下面是我摘出的構造函數最核心的工作內容
整個實例初始化的過程中,重中之重就是把數據 (Model) 和視圖 (View) 建立起關聯關係。Vue.js 和諸多 MVVM 的思路是類似的,主要做了三件事:
- 通過 observer 對 data 進行了監聽,並且提供訂閱某個數據項的變化的能力
- 把 template 解析成一段 document fragment,然後解析其中的 directive,得到每一個 directive 所依賴的數據項及其更新方法。比如 v-text=”message” 被解析之後 (這裏僅作示意,實際程序邏輯會更嚴謹而複雜)
- 通過 watcher 把上述兩部分結合起來,即把 directive 中的數據依賴訂閱在對應數據的 observer 上,這樣當數據變化的時候,就會觸發 observer,進而觸發相關依賴對應的視圖更新方法,最後達到模板原本的關聯效果。
所以整個 vm 的核心,就是如何實現 observer, directive (parser), watcher 這三樣東西
編譯流程
模板解析過程
相關源碼
將template解析爲AST(抽象語法樹)
AST
- 節點類型有三種: ASTElement:tag標籤、ASTText:純文本、ASTExpression: 表達式
- eg:
declare type ASTNode = ASTElement | ASTText | ASTExpression;
declare type ASTElement = {
type: 1;
tag: string;
attrsList: Array<{ name: string; value: string }>;
attrsMap: { [key: string]: string | null };
parent: ASTElement | void;
children: Array<ASTNode>;
static?: boolean; // 靜態節點標記
……
}
compile() 函數
再來看 compile() 函數,這裏是實現模板解析的核心,來做文件 src/compiler/index.js,基本邏輯爲:
// 參數:template, options
ast = parse(template.trim(), options)
optimize(ast, options)
code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
邏輯很清晰,首先從模板進行解析得到抽象語法樹(ast),進行優化,最後生成結果代碼。整個過程中肯定會涉及到 Vue 的語法,包括指令、組件嵌套等等,不僅僅是得到構建 Virtual DOM 的代碼。
需要注意的是,編譯得到 render 其實是代碼文本,通過 new Function(code) 的方式轉爲函數
Vue數據綁定圖解
diff
Vue運行機制小結
- 文本模板,編譯得到生成 vnode 的函數(render),該過程中會識別並記錄 Vue 的指令和其他語法
- new Vue() 得到 vm 對象,其中傳入的數據會進行數據劫持處理,從而可以收集依賴,實現數據綁定
- 渲染過程是將所有數據交由渲染函數(render)進行調用得到 vnode,應該 Virtual DOM 的機制實現初始渲染和更新