1. 程式人生 > >聊聊我對現代前端框架的認知

聊聊我對現代前端框架的認知

http 思想 代碼 -m 介紹 lock alt clas 調用

原文鏈接:mp.weixin.qq.com

奇技指南

本文作者 Berwin,W3C性能工作組成員,360導航高級前端工程師。Vue.js早期用戶,《深入淺出Vue.js》(正在出版)作者。

最近看到一篇國外的文章,說現代JS框架存在的根本原因是保持UI與狀態同步、這其實與我這篇文章的思想是一致的,同時也認證了我對現代前端框架的認知是正確的。

現在前端界有三大框架橫行,Vue,React,Angular,幾乎是所有身為一名前端工程師所必備的一項技能。

但是我不知道有多少人仔細思考過為什麽會這樣?

現在的一些應屆生和剛入行的人們,在剛一踏入前端這個行業起就會面臨著是學習Vue還是學習React又或者是學習Angular等這樣的選擇問題。

事實上在早幾年是沒有這個問題的,我們不需要選擇,那時候我們寫前端就是jQuery一把梭,就是幹,幹就完了。

技術分享圖片

技術分享圖片

那為什麽現在人們需要選擇各種框架了呢?

其實之所以現在我們需要選擇框架,本質上是因為我們面臨的需求變了。大家肯定都明白如果我們只寫一個純展示信息的頁面,沒有任何交互功能的頁面,其實即便是現在,我們也是不需要選擇框架的,我們只需要寫幾行CSS和HTML就可以完成任務。

所以是因為我們面臨的需求變得復雜了,我們的應用經常需要在運行時做一些交互。

這裏面有三個很重要的字我標了粗體,叫做運行時(Runtime)。現代的前端開發,我們開發的應用經常需要在運行時來做一些交互,這些交互在早期只是個幻燈片或者Tab切換下拉菜單等一些簡單的交互,這些交互用jQuery實現完全沒什麽問題。但現代的前端我們的目標是用Web去PK原生應用,去和Native進行PK。

那這個時候我們會發現用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結合變化偵測可以將綁定調整成中等粒度來解決依賴追蹤的開銷問題。

關於變化偵測我專門寫過文章1來介紹Vue是如何實現變化偵測的。

所以變化偵測的方式,在一定程度上就已經決定了框架如何進行渲染。

關於VirtualDOM的實現原理我寫過一個PPT2,有興趣的可以看看。

還有一個是模板編譯,其實前面對於模板編譯這個問題並沒有說太多,模板的作用是描述狀態與DOM之間的映射關系,通過模板可以編譯出一個渲染函數,執行這個渲染函數可以得到VirtualDOM中所提供的VNode,事實上你看過我前面介紹VirtualDOM原理的PPT你就會知道VirtualDOM對節點進行diff其實是對VNode進行diff。關於模板編譯的實現原理我專門寫過一篇文章3介紹過。

最後

最後我想說的話是,現在的前端我個人感覺有點浮躁,很多人都在追新,每天關註一些今天出了一個新特性,明天出了一個新框架什麽的,對於這些我是贊成的,但是我更希望在追新的同時,要看到它的本質。

所有技術解決方案的終極目標都是在解決問題,都是先有問題,然後在有解決方案,解決方案可能並不完美,可能解決方案有很多種,那麽他們之間都有哪些優缺點?解決問題的同時各自都做了哪些權衡和取舍?

我們要透過現象看本質才不至於被表面所迷惑。

聊聊我對現代前端框架的認知