雖然Virtual DOM確實是性能槓槓的,可是其實能夠說它是無意插柳的一個結果。
React的核心思想:
一個Component拯救世界,忘掉煩惱,今後再也不操心界面。
1. Virtual Dom快,有兩個前提
1.1 Javascript很快
Chrome剛出來的時候,在Chrome裏跑Javascript很是快,給了其它瀏覽器很大壓力。而如今通過幾輪你追我趕,各主流瀏覽器的Javascript執行速度都很快了。
Julia有一個Benchmark,Julia Benchmarks, 能夠看到Javascript跟C語言很接近了,也就幾倍的差距,跟Java基本也是一個量級。
因此說,單純的Javascript其實速度是很快的。
多說一句,這種benchmark並非絕對的依據,由於用這個語言寫這個跑得快,並不表明必定是用這個語言寫那個也跑得快。
1.2 DOM很慢
關於什麼CSS,什麼layout那些我不懂,就不瞎說了,咱就說說DOM的結構。
當你用document.createElement()建立一個空的Element的時候(好比建立一個空的div),有如下這幾頁的東西須要實現(固然,這不是標準,只是個大概的意思):
HTMLElement - Web API Interfaces
Element - Web API Interfaces
GlobalEventHandlers
很是很是多,而且還有很多嵌套引用。
你能夠在Chrome console裏手動調用document.createElement 而後插入DOM裏看看效果。
這仍是一個空的Elemnt,啥內容也沒有,就這麼複雜。因此說DOM的操做很是慢是能夠理解的。不是瀏覽器不想好好實現DOM,而是DOM設計得太複雜,沒辦法。
而更糟糕的是,咱們(以及不少框架)在調用DOM的API的時候作得很差,致使整個過程更加的慢。React的Virtual Dom解決的是這一部分問題,它並不能解決DOM自己慢的問題。
好比說,如今你的list是這樣,
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
你想把它變成這樣
<ul>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
一般的操做是什麼?
先把0, 1,2,3這些Element刪掉,而後加幾個新的Element 6,7,8,9,10進去,這裏面就有4次Element刪除,5次Element添加。
而React會把這兩個作一下Diff,而後發現其實不用刪除0,1,2,3,而是能夠直接改innerHTML,而後只須要添加一個Element(10)就好了,這樣就是4次innerHTML操做加1個Element添加,比9次Element操做快多了吧?
固然還有其它一些例子可以優化咱們對DOM的操做,就不舉例子了。(其實是由於我舉不出例子。。。)
2. 關於React
2.1 接口和設計
在React的設計裏,是徹底不須要你操做DOM的。在React裏其實根本就沒有DOM這個概念的存在,只有Component。當你寫好一個Component之後,Component會徹底負責UI,你不須要也不該該去也不可以指揮Component怎麼顯示,你只能告訴它你想要顯示一個香蕉仍是兩個梨。
隔離DOM並非由於DOM慢(固然DOM確實慢),而是把界面和業務徹底隔離,操做數據的只關心數據,操做界面的只關心界面。能夠想象成把MVC裏面的Controller分紅兩個部分,一部分合併到M裏面去,一部分合併到V裏面去,就剩下MV,沒有C了。。。其實M也並非Model了。推薦看一下Pete Hunt的這個Talk https://youtu.be/DgVS-zXgMTk
重複一遍,React的意思是,我提供一個Component,而後你只管給我數據,界面的事情徹底不用你操心,我保證會把界面變成你想要的樣子。
你能夠把一個React的Component想象成一個Pure Function,只要你給的數據是[1, 2, 3],我保證顯示的是[1, 2, 3]。沒有什麼刪除一個Element,添加一個Element這樣的事情。NO。你要我顯示什麼就給我一個完整的列表。
說到這裏,插一句別的,我一開始看到這裏還覺得這樣的處理方式比較適合通常的WEB應用,寫遊戲啊什麼的可能這個模式不太好用,而後我就看到Pete Hunt那個Talk,說DOOM 3就是這麼幹的。
。
。。
。。。
眼淚都下來了,大神們的思路果真我是摸不着邊的,洗洗睡吧。
睡醒了接着說。React其實須要從Imperative Programming轉換到Declarative Programming去理解。你不要一步一步告訴我這件事情怎麼作,什麼先和麪再剁餡,NO,告訴我你想要煎餅仍是月餅,我會想辦法去作的,不要來干擾我。你只須要告訴我有這麼一個列表[1, 3, 6]須要顯示就好了,不要告訴我怎麼顯示,我會想辦法的,我保證美得冒泡,各類神奇的效果,亮瞎你的鈦合金狗眼。
行了行了,你真囉嗦。
。。。
再說幾句瞎扯的話,Flux雖說的是單向的Data Flow,可是實際上就是單向的Observer。
Store->View->Action->Store(箭頭是數據流向,實現上能夠理解爲View監聽Store,View直接trigger action,而後Store監聽Action)
等等,不是說Component是pure function不跟誰綁定嗎,爲啥View要監聽Store?你這個騙子。怪不得都沒有人給你點贊。
。。。
。。
。
咱們仍是繼續說React把,Flux是什麼鬼,我反正沒聽過。
2.2 實現
OK,那麼,如何實現React呢?
其實對於React來講,最容易實現的辦法是每次徹底摧毀整個DOM,而後從新創建一個全新的DOM。由於一個Component是一個Pure function,根本就沒有State這個概念,我又不知道DOM如今是什麼樣子,那最簡單的辦法固然是隻要你給新數據,我就把整個DOM刪了,而後根據你給的數據從新生成一個DOM咯。
等等,Virtual DOM哪兒去了?
事實是這樣的,最簡單實現React的方式雖說很是簡單,可是效率實在是過低了,你竟然要所有都刪了重建DOM,DOM自己已經很慢了,你還這麼去用,誰能忍啊?
而後Virtual DOM就來救場了。
Virtual DOM和DOM是啥關係呢?
首先,Virtual DOM並無徹底實現DOM,Virtual DOM最主要的仍是保留了Element之間的層次關係和一些基本屬性。由於DOM實在是太複雜,一個空的Element都複雜得能讓你崩潰,而且幾乎全部內容我根本不關心好嗎。因此Virtual DOM裏每個Element實際上只有幾個屬性,而且沒有那麼多亂七八糟的引用。因此哪怕是直接把Virtual DOM刪了,根據新傳進來的數據從新建立一個新的Virtual DOM出來都很是很是很是快。(每個component的render函數就是在作這個事情,給新的virtual dom提供input)
因此,引入了Virtual DOM以後,React是這麼幹的:
你給我一個數據,我根據這個數據生成一個全新的Virtual DOM,而後跟我上一次生成的Virtual DOM去 diff,獲得一個Patch,而後把這個Patch打到瀏覽器的DOM上去。完事。
有點像版本控制打patch的思路。
假設在任意時候有,VirtualDom1 == DOM1 (組織結構相同)
當有新數據來的時候,我生成VirtualDom2,而後去和VirtualDom1作diff,獲得一個Patch。
而後將這個Patch去應用到DOM1上,獲得DOM2。
若是一切正常,那麼有VirtualDom2 == DOM2。
這裏你能夠作一些小實驗,去破壞VirtualDom1 == DOM1這個假設(手動在DOM裏刪除一些Element,這時候VirtualDom裏的Element沒有被刪除,因此兩邊不同了)。
而後給新的數據,你會發現生成的界面就不是你想要的那個界面了。
最後,回到爲何Virtual Dom快這個問題上。
實際上是因爲每次生成virtual dom很快,diff生成patch也比較快,而在對DOM進行patch的時候,我可以根據Patch的內容,優化一部分DOM操做,好比以前1.2裏的那個例子。
重點就在最後,哪怕是我生成了virtual dom,哪怕是我跑了diff,可是我根據patch簡化了那些DOM操做省下來的時間依然很可觀。因此整體上來講,仍是比較快。
簡單發散一下思路,若是哪一天,DOM自己的已經操做很是很是很是快了,而且咱們手動對於DOM的操做都是精心設計優化事後的,那麼加上了VirtualDom還會快嗎?
固然不行了,畢竟你多作了這麼多額外的工做。
可是那一天會來到嗎?
誒,大不了到時候不用Virtual DOM。瀏覽器