首先歡迎你們關注個人掘金帳號和Github博客,也算是對個人一點鼓勵,畢竟寫東西無法得到變現,能堅持下去也是靠的是本身的熱情和你們的鼓勵。html
之因此想寫本系列文章的主要緣由是將近一個月時間沒有寫點東西了,加上最近各類事情特別多使得我沒有過多的時間研究本身喜歡的東西。前段時間看到大神livoras的博客,關於Virtual DOM的講解的很是透徹,因爲本身從事React開發,也算是Virtual DOM的使用者,因此萌發了寫關於Virtual DOM的系列文章,而且也想嘗試本身實現一套Virtual DOM。所以本系列文章會圍繞這Virtaul DOM這個話題作一系列的分析甚至也會嘗試着和你們一塊兒實現本身的Virtual DOM 框架,感興趣的同窗能夠關注的個人掘金帳號或者關注個人Github博客。前端
首先聽到Virtual DOM這個概念應該來自於React,而且在不瞭解時以爲這個概念是一個逼格特別高的詞。其實任何技術的誕生都是有相應的歷史的,沒有任何事物是憑空出現的,就像我聽到不少人詬病JavaScript語言的語法糟粕太多,但實質上你要了解到JavaScript出現的緣由和它的做者Brendan Eich僅僅用了十幾天就設計出一門普遍流行的高級語言,你必定不會這樣想。一樣的,Virtual DOM的出現也是有必定的歷史緣由的,這就不得不講到前端框架的歷史了。git
其實全部框架所能作到的事情咱們手動均可以實現,對於咱們在大學課堂(固然有的學習並無開設相關的課程)的學習JavaScript時候,想用JavaScript建立一個表單驗證的程序時,十幾行代碼就能搞定,這時候框架這種東西對你是臃腫的、沒有必要的。甚至框架會極大的提升你的學習成本、下降你程序的運行速度(框架並不能保證效率必定高於你的手動操做)。可是當你的程序規模逐漸增大,你會發現你的代碼數量會指數級膨脹,甚至一個js文件中會有上萬行的代碼(不要驚訝,我真的見過這場常見),這時候維護這套代碼將是一場災難。github
這時候各類前端框架就應用而生了,框架出現的目的並非爲了提高性能,而是爲了可維護性、爲了便於團隊開發。可是天下沒有白吃的午飯,你爲了程序的可維護性,出讓了一部分性能做爲妥協,畢竟什麼框架都沒有手動原生操做性能高,由於框架要具備適普性,要能處理各類各樣的場景。算法
其實對於前端所須要的作的就是展現數據的界面(View)以及在界面的更改能觸發相應的數據(Model)變化,而且數據(Model)發生改變時界面(View)也能及時響應並作相應的變化。說到底就是如何協調View與Modal的關係。 緩存
早期出現相似於Backbone的框架就是典型的MVC(其實我也並無經歷過這個年代)。經過在View與Model設置Controller層,這樣用戶交互觸發的操做都會轉交給Controller,由Controller層控制相應的改變Model的數據。在Model數據發生改變時,會經過觀察者模式去通知對應的View,而後View從新請求Model數據作相應的界面改變。前端框架
隨着應用規模的增長你會發現MVC模式會存在幾個顯著的問題,Model與View的對應關係是多對多的,可能一個Model會對應多個View或者一個View對應多個Model,甚至更加複雜的場景,Model與View之間錯綜複雜的關係使得開發的難度增長。而且因爲View是依賴於Model的,所以想要在這種模式下實現視圖的組件化是相對比較難的。框架
咱們並不但願View和Model之間依賴的這麼緊,因此咱們能夠改進MVC模式,就出現了MVP模式。 組件化
MVP是MVC的改進版本,將MVC中的Controller改成Presenter,用戶交互觸發的操做會轉交給Presenter處理。而後會由Presenter控制改變相應Model的改變。到此爲止Presenter所承擔的操做與Controller很是相近,可是爲了讓Model與View相互獨立,Model改變後的通知是分發給Presenter,Presenter收到Model改變的通知就會調用View的接口來改變用戶界面。這樣咱們經過Presenter就實現了Model和View的相互獨立,只要View與Model之間預留好所須要的接口,兩者互相是沒有影響關係,互相是透明的,在這個基礎上,咱們要實現View的組件化是很是容易的。可是這種模式也並非沒有缺點,Presenter的邏輯不只須要承擔以前Controller的全部功能,並且還須要接受Model數據改變的通知並按照對應的功能改變用戶界面,這就使得Presenter所要承擔的功能過於多,使得Presenter太臃腫、難以維護。性能
咱們發現MVP也有相應的缺點,所以前人在MVP的基礎上作了改良,出現了MVVM的結構。
咱們看到MVP的缺點主要是Presenter過於龐大,其實Model改變通知Presenter而且Presenter改變View這條邏輯並非必定須要手動控制,其實對應的Model變化引發對應固定的View改變的規則通常來說是不變的,那這一部分邏輯就能夠釋放出來由引擎自動處理。MVP按照這個思路進行改良,將原來的Presenter進化爲View Of Model(VM:視圖模型),視圖模型中包含Binder(或者說是Data-binding engine),負責View與Model的雙向綁定。在編寫View時可使用聲明式的指令將View與對應的Model進行綁定。ViewModel在對應Model進行改變時能夠自動更新View,同理View發生改變時,ViewModel也會對應Model的數據,這也就是咱們一般所講雙向數據綁定(Two-way data-binding)。MVVM的優勢很是顯然,極大的提升了可維護性,View與Model之間的相互手動維護更新被釋放,改成自動更新。可是因爲ViewModel的構建和維護成本相對較高,對於一些簡單的頁面並不適用,對於複雜的視圖卻又帶來了性能成本。
到此爲止咱們瞭解了MV* 類型的框架是如何解決Model層與View層鏈接的,經過在Model與View之間構建各類中間層(Controller、Presenter、View of Model)來處理二者之間的同步關係。可是咱們能不能換一種思路,View能夠當作Model對應必定規則的視圖表示,所以當Model發生改變時直接從新渲染View。
咱們不由感到這種操做方式真是騷啊!
這種方法行不行?固然!其實React的整體思路不就是這種嗎?可是經驗豐富的你必定會馬上質疑這東西性能靠譜嗎?事實上,若是Model改變引發的View改變很是大(譬如全部的界面都改變了),這種模式反而性能會很好,由於自己的界面改變就等同於從新渲染。可是若是當前的Model改變只會引發界面一個很是細微的變化(例如某個按鈕的顏色發生改變),咱們就從新刷新整個界面,那實在是太恐怖了。
搞過前端的同窗必定都明白DOM的速度相比於JavaScript的簡直就是龜速,由於DOM的屬性、結構自己就設計的很是複雜。那這種方式是否是就徹底能夠廢棄呢?
不是!不然React就不會出現。
大學學習過計算機組成原理的同窗應該還記的,CPU的計算速度是很是快,可是相比於CPU,其餘的IO部件,例如硬盤,速度是很是低的,差的都不是一個數量級的問題。這時候計算機就引入了RAM,RAM的速度低於CPU可是卻高於硬盤,對於CPU所需的數據,能夠先從硬盤放入RAM,而後CPU運算的結果也放入RAM中,若是須要數據的永久化存儲時纔會存入硬盤中。甚至CPU會以爲RAM速度仍是慢,在CPU內部引進了各級Cache,來解決RAM與CPU之間的性能瓶頸。
前端就能夠借鑑這種思惟方式,DOM是低速的,但JavaScript性能確實至關的不錯,尤爲在擁有V8引擎的今天。那麼咱們就能夠用JavaScript來描述DOM結構,相似於下面的結構:
其實所描述的DOM結構就是下面的樣子:
<div>
<button>按鈕</button>
</div>
複製代碼
固然這種表示方法並不惟一,只要能描述清對應的DOM結構,你能夠隨意發揮。
這樣在每次Model改變以後,咱們首先能夠拿到本次Model的對應的Virtual DOM結構,它表明的就是本次Model對應View的DOM結構。若是咱們還在程序中緩存了上次Model對應的Virtual DOM結構,那麼咱們就能夠去比較先後兩個Virtual DOM結構,採用必定的算法,得出兩個Virtual DOM的不一致的地方,這個算法就是咱們一般所講的Diff算法。而後用最優的方法將兩個Virtual DOM的差別應用的真實DOM上。
那這種方式必定是高於咱們最開始所講的從新渲染的思路嗎?固然不是,若是界面變化很是大,那麼咱們以前所講的Virtual DOM性能可能就低於從新渲染,由於Diff的過程也是有性能損耗了,即便在React採用了各類啓發式算法將兩個Virtual DOM樹形結構由O(n ^ 3)降到了O(n)的狀況下,在節點很是多的狀況下,Diff的代價也是要被考慮的。
本篇文章咱們大體環顧了各類MV*框架的改進歷史,從而講述了React所提出的另外一套新穎的解決思路,而且爲何React會引入Virtual DOM的緣由。讀到這裏可能對你瞭解Virtual DOM有了些許幫助,歡迎你們關注個人博客,之後會繼續更新Virtual DOM的系列文章以及其餘我在前端學習中感悟。若有不正確的地方,歡迎各位賜教。