編者按:本文做者 Berwin,W3C性能工做組成員,360導航高級前端工程師。Vue.js早期用戶,《深刻淺出Vue.js》(正在出版)做者。javascript
最近看到一篇國外的文章,說現代JS框架存在的根本緣由是保持UI與狀態同步、這其實與我這篇文章的思想是一致的,同時也認證了我對現代前端框架的認知是正確的。前端
如今前端界有三大框架橫行,Vue,React,Angular,幾乎是全部身爲一名前端工程師所必備的一項技能。java
可是我不知道有多少人仔細思考過爲何會這樣?git
如今的一些應屆生和剛入行的人們,在剛一踏入前端這個行業起就會面臨着是學習Vue仍是學習React又或者是學習Angular等這樣的選擇問題。github
事實上在早幾年是沒有這個問題的,咱們不須要選擇,那時候咱們寫前端就是jQuery一把梭,就是幹,幹就完了。前端框架
其實之因此如今咱們須要選擇框架,本質上是由於咱們面臨的需求變了。你們確定都明白若是咱們只寫一個純展現信息的頁面,沒有任何交互功能的頁面,其實即使是如今,咱們也是不須要選擇框架的,咱們只須要寫幾行CSS和HTML就能夠完成任務。微信
因此是由於咱們面臨的需求變得複雜了,咱們的應用常常須要在運行時作一些交互。前端工程師
這裏面有三個很重要的字我標了粗體,叫作運行時(Runtime)。現代的前端開發,咱們開發的應用常常須要在運行時來作一些交互,這些交互在早期只是個幻燈片或者Tab切換下拉菜單等一些簡單的交互,這些交互用jQuery實現徹底沒什麼問題。但現代的前端咱們的目標是用Web去PK原生應用,去和Native進行PK。app
那這個時候咱們會發現用jQuery來開發應用,咱們的代碼變得很難以維護,那爲何使用現代框架好比Vue,React等就變得容易維護了呢?框架
這裏面請容我講一個故事,一個小插曲,前幾天我在一個微信羣裏面有人討論,Vue和jQuery的區別是什麼,有人很是強烈的說什麼差異是Vue有組件,有什麼這個那個的一些特性。
當時我在微信羣裏說了個人觀點,我說Vue和jQuery之間的區別只有一點,聲明式與命令式。
咱們能夠想一下,咱們用jQuery去操做DOM的目的是什麼?是爲了局部更新視圖,換句話說是爲了局部從新渲染。
jQuery是命令式的操做DOM,命令式的局部更新視圖,而現代主流框架Vue,React,Angular等都是聲明式的,聲明式的局部更新視圖。
爲何聲明式的操做DOM就可讓應用變得好維護了呢?
弄明白這個問題首先咱們先簡單介紹下什麼是命令式,什麼是聲明式。
命令式,像jQuery,咱們都是想幹什麼而後就去幹就完了,例以下面的代碼:
$('.box')
.append('<p>Test</p>')
.xxx()
.yyy()
.jjj()
...
複製代碼
命令式就是想幹什麼就直接去調用方法直接幹就完了,簡單直接粗暴。
試想一個很簡單的場景,好比一個toggle效果,點擊一個按鈕,切換顏色。
用命令式寫,咱們確定是這樣寫,若是當前是什麼顏色就讓它變成另一個顏色。
若是你仔細思考,其實這裏面能夠細分紅兩個行爲,一個是對狀態判斷,另外一個是操做DOM。
那什麼是聲明式??
聲明式是經過描述狀態與視圖之間的映射關係,而後經過這樣的一個映射關係來操做DOM,或者說具體點是用這樣的映射關係來生成一個DOM節點插入到頁面去。好比Vue中的模板。模板的做用就用是來描述狀態與DOM的映射關係。
一樣的場景,咱們用Vue中的模板來實現,當咱們用模板描述了映射關係以後,咱們在點擊按鈕時,咱們只須要對顏色這個變量進行修改就能夠完成需求。
看到區別了麼?
仔細思考下,用Vue來實現一樣的需求,若是細分來看,咱們在邏輯上只有一個行爲,只有狀態。而jQuery是兩個行爲,狀態+DOM操做。
因此聲明式爲何能夠簡化維護應用代碼的複雜度?
由於它讓咱們能夠把關注點只放在狀態的維護上。這樣一來當應用複雜後,其實咱們的思惟,咱們管理代碼的方式只在狀態上,全部的DOM操做都不用關心了,能夠說大大下降代碼維護的複雜度。
咱們再也不須要關注怎麼操做DOM,由於框架會幫咱們自動去作,咱們只關注狀態就行了。
可是若是應用特別特別複雜,咱們會發現即使是咱們只關注狀態的維護,依然很難,即使只維護狀態也很難,因此纔出現了Vuex,Redux等技術解決方案。
通過前面的介紹,你會發現其實現代主流框架要解決的最本質的問題依然是渲染,只是不一樣框架之間的解決方案有差別,那麼什麼是渲染?
如今開發前端,咱們的應用在運行時須要不斷的進行各類交互,現代主流框架讓咱們把關注點放在了狀態的維護上,也就是說應用在運行時,應用內部的狀態會不斷的發生變化。
而將狀態生成DOM插入到頁面展現在用戶界面上,這一套流程叫作渲染。
當應用在運行時,內部狀態會不斷的發生變化,這時用戶頁面的某個局部區域須要不停的從新渲染。
如何從新渲染?
最簡單粗暴的解決方式,也是我平時在沒有使用任何框架的項目裏寫的一些簡單的功能時最經常使用的方式是用狀態生成一份新的DOM,而後用innerHTML把舊DOM替換了。
我寫的小功能塊用這種方式沒問題,由於功能涉及到的DOM標籤少,狀態變的時候,幾乎就是我這個功能塊的全部標籤都須要變,因此即使是用innerHTML也不會有太大的性能浪費,是在可接受範圍內的。
可是框架不行,框架若是用innerHTML這樣去替換,那就不是局部從新渲染了,而是整個頁面總體刷新,這性質就變了,那麼框架如何作到局部從新渲染?
解決這個問題,須要一些技術方案來解決,能夠是VirtualDOM,但並不必定必須是VirtualDOM,也能夠是Angular中的髒檢測的流程,也能夠是細粒度的綁定,像Vue1.0就是使用細粒度的綁定來實現的。
什麼是細粒度綁定?
細粒度的綁定意思是說,當某個狀態,與之綁定的是頁面中的某個具體的標籤。就是說,若是模板中有十個標籤使用了某個變量,那麼與這個變量所綁定的就是10個具體的標籤。
相對比較React和Angular粒度都比較粗,他們的變化偵測其實不知道具體哪一個狀態變量,因此須要一個暴力的比對,比對後才知道須要對視圖中的哪一個部分進行更新。
而Vue這種細粒度的綁定其實在狀態發生變化的那一個瞬間,馬上就知道哪一個狀態變了,並且還知道有哪些具體的標籤使用了這個狀態,那麼事情就變的簡單的多了,直接把與這個狀態所綁定的這些具體的標籤進行更新就能達到局部更新的目的。
可是這樣作其實也有必定的代價,由於粒度太細,會有必定的依賴追蹤的開銷。因此Vue2.0開始採起了一個折中的方案,就是把綁定調整爲中等粒度。
一個狀態對應某個組件,而再也不是具體標籤,這樣作有一個好處是能夠大大下降依賴的數量,畢竟組件的數量與DOM中的具體標籤比,數量要少的多。可是這樣就須要多一個操做,當狀態發生變化只通知到組件,那麼組件內部如何知道具體更新哪一個DOM標籤??
答案是VirtualDOM。
也就是說,當粒度調整爲中等以後,須要多一個操做就是在組件內部使用VirtualDOM去從新渲染。
Vue很聰明地經過變化偵測+VirtualDOM這兩種技術方案,提高了框架運行的性能問題。
因此說,Vue2.0引入VirtualDOM並非由於VirtualDOM有多好,而是剛好VirtualDOM結合變化偵測能夠將綁定調整成中等粒度來解決依賴追蹤的開銷問題。
關於變化偵測我專門寫過文章來介紹Vue是如何實現變化偵測的。傳送門。
因此變化偵測的方式,在必定程度上就已經決定了框架如何進行渲染。
關於VirtualDOM的實現原理我寫過一個PPT,有興趣的能夠看看,傳送門。
還有一個是模板編譯,其實前面對於模板編譯這個問題並無說太多,模板的做用是描述狀態與DOM之間的映射關係,經過模板能夠編譯出一個渲染函數,執行這個渲染函數能夠獲得VirtualDOM中所提供的VNode,事實上你看過我前面介紹VirtualDOM原理的PPT你就會知道VirtualDOM對節點進行diff實際上是對VNode進行diff。關於模板編譯的實現原理我專門寫過一篇文章介紹過,傳送門。
最後我想說的話是,如今的前端我我的感受有點浮躁,不少人都在追新,天天關注一些今天出了一個新特性,明天出了一個新框架什麼的,對於這些我是同意的,可是我更但願在追新的同時,要看到它的本質。
全部技術解決方案的終極目標都是在解決問題,都是先有問題,而後在有解決方案,解決方案可能並不完美,可能解決方案有不少種,那麼他們之間都有哪些優缺點?解決問題的同時各自都作了哪些權衡和取捨?
咱們要透過現象看本質纔不至於被表面所迷惑。
《奇舞週刊》是360公司專業前端團隊「奇舞團」運營的前端技術社區。關注公衆號後,直接發送連接到後臺便可給咱們投稿。