自己實現MVVM(Vue原始碼解析)

2021-09-17 07:00:43 字數 3675 閱讀 2962

本文會帶大家手動實現乙個雙向繫結過程(僅僅涵蓋一些簡單的指令解析,如:v-textv-model,插值),當然借鑑的是vue1的原始碼,相信大家在閱讀完本文後對vue1會有乙個更好的理解,源**放到了github,由於本人水平有限,理解不到位的地方還請大家指出。

mvvm使開發可以更加關注於資料,減少了很大的工作量,也使**可讀性,可維護性更高,mvvm核心的思想就是檢視是狀態的函式:view = viewmodel(model),所以當model發生改變時,viewmodel會來操作view來怎麼做,而非是自己寫**來做。無論是雙向繫結還是單向繫結,都是符合mvvm思想的。vue提倡的是雙向繫結,也就是允許view到model的變化,其實這個場景出現在的也就是表單操作上,看個例子,例子中分別利用了vue和react實現了一下表單value變化,影響頁面與其相關的dom節點發生變化,可以發現的是雙向繫結的vue是inputvalue發生變化則h1innertext就發生了變化,變化是由view->model,而提倡單向資料流的react需要手動監聽事件,事件觸發後,更改model的值,從而使inputvalue發生了變化。看了vue的原始碼後不難發現vue的雙向繫結的實現也就是在表單元素上新增了input事件,可以說雙向繫結是單向繫結的乙個語法糖。

上圖是乙個大體的流程,下面按照流程來實現下:

這點的實現,需要借助的是object.defineproperty()來為物件的屬性繫結get/set特性(由於利用了object.defineproperty(),所以vue不支援ie8),observer需要將data的所有屬性都繫結get/set,很容易想到的就是利用遞迴來實現,具體**就不貼出,請參見這裡。

這點實現的是將我們的模板轉化為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是乙個指令集合,也就是決定了如何初始化以及如何更新該節點。對於nodetype1的節點來說,指令全部儲存在其屬性中,遍歷屬性,假若指令中含有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節點相關聯,利用的就是depwatcher,頁面上每乙個與資料相關聯的節點都含有乙個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的大...