1. 程式人生 > >“漸進式框架”和“自底向上增量開發的設計”這兩個概念是什麼?(轉) 留任自己記錄

“漸進式框架”和“自底向上增量開發的設計”這兩個概念是什麼?(轉) 留任自己記錄

徐飛

在我看來,漸進式代表的含義是:主張最少。

每個框架都不可避免會有自己的一些特點,從而會對使用者有一定的要求,這些要求就是主張,主張有強有弱,它的強勢程度會影響在業務開發中的使用方式。

比如說,Angular,它兩個版本都是強主張的,如果你用它,必須接受以下東西:

  • 必須使用它的模組機制
  • 必須使用它的依賴注入
  • 必須使用它的特殊形式定義元件(這一點每個檢視框架都有,難以避免)

所以Angular是帶有比較強的排它性的,如果你的應用不是從頭開始,而是要不斷考慮是否跟其他東西整合,這些主張會帶來一些困擾。

比如React,它也有一定程度的主張,它的主張主要是函數語言程式設計的理念,比如說,你需要知道什麼是副作用,什麼是純函式,如何隔離副作用。它的侵入性看似沒有Angular那麼強,主要因為它是軟性侵入。

你當然可以只用React的檢視層,但幾乎沒有人這麼用,為什麼呢,因為你用了它,就會覺得其他東西都很彆扭,於是你要引入Flux,Redux,Mobx之中的一個,於是你除了Redux,還要看saga,於是你要糾結業務開發過程中每個東西有沒有副作用,純不純,甚至你連這個都可能不能忍:

const getData = () => {
// 如果不存在,就在快取中建立一個並返回
// 如果存在,就從快取中拿
}

因為你要糾結它有外部依賴,同樣是不加引數呼叫,連續兩次的結果是不一樣的,於是不純。

為什麼我一直不認同在中後臺專案中使用React,原因就在這裡,我反對的是整個業務應用的函式式傾向,很多人都是看到有很多好用的React元件,就會傾向於把它引入,然後,你知道怎麼把自己的業務對映到函式式的那套理念上嗎?

函數語言程式設計,無副作用,寫出來的程式碼沒有bug,這是真理沒錯,但是有兩個問題需要考慮:

  1. JS本身,有太多特性與純函式式的主張不適配,這一點,題葉能說得更多
  2. 業務系統裡面的實體關係,如何組織業務邏輯,幾十年來積累了無數的基於設計模式的場景經驗,有太多的東西可以模仿,但是,沒有人給你總結那麼多如何把你的厚重業務對映到函式式理念的經驗,這個地方很考驗綜合水平的,真的每個人都有能力去做這種對映嗎?

函數語言程式設計無bug的根本就在於要把業務邏輯完全都依照這套理念搞好,你看看自己公司做中後臺的員工,他們熟悉的是什麼?是基於傳統OO設計模式的這套東西,他們以為拿著你們給的元件庫就得到了一切,但是可能還要被灌輸函數語言程式設計的一整套東西,而且又沒人告訴他們在業務場景下,如何規劃業務模型、組織程式碼,還要求快速開發,怎麼能快起來?

所以我真是心疼這些人,他們要的只是元件庫,卻不得不把業務邏輯的思考方式也作轉換,這個事情沒有一兩年時間洗腦,根本洗不到能開發業務的程度。

沒有好元件庫的時候,大家痛點在檢視層,有了基於React的元件化,把原先沒那麼痛的業務邏輯部分搞得也痛起來了,原先大家按照設計模式教的東西,照貓畫虎還能繼續開發了,學了一套新理念之後,都不知道怎麼寫程式碼了,怎麼寫都懷疑自己不對,可怕。

我寧可支援Angular也不支援React的原因也就在此,Angular至少在業務邏輯這塊沒有軟主張,能夠跟OO設計模式那套東西配合得很好。我面對過很多商務場景,都是前端很厚重的東西,不僅僅是管理控制檯這種,這類東西里面,業務邏輯的佔比要比檢視大挺多的,如何組織這些東西,目前幾個主流技術棧都沒有解決方案,要靠業務架構師去擺平。

如果你的場景不是這麼厚重的,只是簡單管理控制檯,那當我沒說好了。

框架是不能解決業務問題的,只能作為工具,放在合適的人手裡,合適的場景下。

現在我要說說為什麼我這麼支援Vue了,沒什麼,可能有些方面是不如React,不如Angular,但它是漸進的,沒有強主張,你可以在原有大系統的上面,把一兩個元件改用它實現,當jQuery用;也可以整個用它全家桶開發,當Angular用;還可以用它的檢視,搭配你自己設計的整個下層用。你可以在底層資料邏輯的地方用OO和設計模式的那套理念,也可以函式式,都可以,它只是個輕量檢視而已,只做了自己該做的事,沒有做不該做的事,僅此而已。

漸進式的含義,我的理解是:沒有多做職責之外的事。

前言:框架是什麼?為什麼要有框架?在眾多的框架之中,Vue獨具魅力之處在哪裡呢?其背後的核心思想是什麼?Vue究竟火到什麼程度?最近釋出的Vue2.0又做了哪些改進呢?Vue和Weex又是怎樣的一種合作?

2016年10月20日,Vue Technology LLC 創始人, Vue.js作者尤雨溪在QCon上海做了題為《Vue 2.0——漸進式前端解決方案》的演講,對上述問題一一進行了闡述。

一、為什麼要有框架
1. 框架的存在是為了幫助我們應對複雜度
前端框架特別多,那麼為什麼要有框架呢?我個人的看法是,框架的存在是為了幫助我們應對複雜度。當我們需要解決一些前端上工程問題的時候,這些問題會有不同的複雜度。如果你用太簡陋的工具應對非常複雜的需求,就會極大地影響你的生產力。所以,框架本身是幫我們把一些重複的並且已經受過驗證的模式,抽象到一個已經幫你設計好的API封裝當中,幫助我們去應對這些複雜的問題。

  1. 框架自身也有複雜度
    但是,框架本身也會帶來複雜度。相信大家在調研各種框架或學習各種框架時,會遇到學習曲線問題——有些框架會讓人一時不知如何上手。

這裡就抽象出一個問題,就是要做的應用的複雜度與所使用的框架的複雜度的對比。

相關廠商內容

百度AI技能渠道體系首席研究員的重構分享分還是合?58到家訂單中心架構演進架構優化!愛奇藝面對流量洪峰的三板斧摩拜單車:如何通過機器學習等技術提高單車運營效率如何基於AWS搭建大資料統計分析系統
相關贊助商

進一步說,是所要解決的問題的內在複雜度,與所使用的工具的複雜度進行對比。

  1. 工具複雜度是為了處理內在複雜度所做的投資
    工具的複雜度是可以理解為是我們為了處理問題內在複雜度所做的投資。為什麼叫投資?那是因為如果投的太少,就起不到規模的效應,不會有合理的回報。這就像創業公司拿風投,投多少是很重要的問題。如果要解決的問題本身是非常複雜的,那麼你用一個過於簡陋的工具應付它,就會遇到工具太弱而使得生產力受影響的問題。

反之,是如果所要解決的問題並不複雜,但你卻用了很複雜的框架,那麼就相當於殺雞用牛刀,會遇到工具複雜度所帶來的副作用,不僅會失去工具本身所帶來優勢,還會增加各種問題,例如培訓成本、上手成本,以及實際開發效率等。

  1. Pick the right tool for the job
    “Pick the right tool for the job”——在國外,跟開發者討論一些框架選型問題時,大家都會說這句話——一切都要看場景。因為,前端開發原生開發或者桌面開發模式相比,有自己的獨特之處,它跟其實並不那麼固定。在Web上面,應用可以有非常多的形態,不同形態的Web應用可能有完全不同程度的複雜度。這也是為什麼我要談工具複雜度和所要做的應用複雜度的問題。

  2. 怎麼看前端框架的複雜度
    目前的前端開發已經越來越工程化,而我們需要解決的實際問題也是不同的。

如下圖所示,我們可能在任何情況下都需要宣告式的渲染功能,並希望儘可能避免手動操作,或者說是可變的命令式操作,希望儘可能地讓DOM的更新操作是自動的,狀態變化的時候它就應該自動更新到正確的狀態;我們需要元件系統,將一個大型的介面切分成一個一個更小的可控單元;客戶端路由——這是針對單頁應用而言,不做就不需要,如果需要做單頁應用,那麼就需要有一個URL對應到一個應用的狀態,就需要有路由解決方案;大規模的狀態管理——當應用簡單的時候,可能一個很基礎的狀態和介面對映可以解決問題,但是當應用變得很大,涉及多人協作的時候,就會涉及多個元件之間的共享、多個元件需要去改動同一份狀態,以及如何使得這樣大規模應用依然能夠高效執行,這就涉及大規模狀態管理的問題,當然也涉及到可維護性,還有構建工具。現在,如果放眼前端的未來,當HTTP2普及後,可能會帶來構建工具的一次革命。但就目前而言,尤其是在中國的網路環境下,打包和工程構建依然是非常重要且不可避免的一個環節。

二、主流框架分析
我們看一下現有的一些主流框架從少到多所解決的問題。這個多少並不是來評價框架的好壞,而是從設計的角度出發看它涵蓋多少內容。

純模板引擎:最少的就是純模板引擎,只管狀態到介面的對映。

React和Vue:其實這兩者都是非常專注的只做狀態到介面對映,以及元件。

Backbone:它會給你多一些架構上指導,比如它會讓你分層。

Angular:它做的事情就更多,它有自己的路由,這些都會包含在裡面。

Ember:相比Angular,Ember做得就更加徹底,Ember信奉的是約定優於配置,它會將一切都幫你設計好打包好,你就開箱用就可以了。

Meteor:Meteor只是一個極端,它是從前到後全都包含,從前端到資料層到資料庫,全都幫你打包好。

通過簡單的分析,我們可以感受到,做得少的框架不一定就不如做得多的框架,這體現出一種取捨。也就是說,做得少的框架可以給你更多的靈活性,但你需要做更多的選擇;做得多的框架有更強的侵入性,學習成本更高,靈活性更低。一旦選擇了一個侵入性強的框架,那麼一些小的部分你就沒有機會去切換成其他你更想用的方案。

所以,React和Vue有一個共同特點,它們都有各自的配套工具,核心雖然只解決一個很小的問題,但它們有生態圈及配套的可選工具,當你把他們一個一個加進來的時候,就可以組合成非常強大的棧,就可以涵蓋其他的這些更完整的框架所涵蓋的問題。

這樣的一個配置方案,使得在你構建技術棧的時候有可彈性伸縮的工具複雜度:當所要解決的問題內在複雜度很低的時候,可以只用核心的這些很簡單的功能;當需要做一個更復雜的應用時,再增添相應的工具。例如做一個單頁應用的時候才需要用路由;做一個相當龐大的應用,涉及到多元件狀態共享以及多個開發者共同協作時,才可能需要大規模狀態管理方案。

一個純粹的複雜的單頁應用,和只是在後端渲染的靜態頁面上嵌入互動內容所需要選擇的工程棧其實是有相當大區別的。這就是為什麼我覺得,核心+生態的棧會是一個在整體選型更為靈活的棧。

React和Vue都選擇這個模式。Facebook團隊只是專注做React本身,但React社群非常活躍,貢獻了大量的第三方解決方案。不可否認,React社群是當前最活躍的社群,很多優秀的想法和思路,包括狀態管理方案最早都是從React社群萌發出來。但是社群的這種活躍也帶來一定程度的副作用,那就是時代變化太快,三天出一個新版本,同一個問題曾經存在幾十種不同的解決方案。這就使得我們在去搭建自己的棧時,需要花很多的時間去鑑別所選擇的部件。同時,由於整個生態圈的這些庫並不是由一個統一的團隊去規劃和設計的,所以很難考慮到不同的庫之間的協作,這就會導致磨合問題。

同時,這也使得很多開發者抱怨有一個“JavaScript Fatigue”,說JS生態圈東西太多了,為了跟上潮流,需要不停學習最新的東西,覺得很累。當然,有很多人覺得這是生態圈繁榮的表現,但它確實使得大家面臨了選擇困難症的問題。

Dan Abramov 是之前React社群非常活躍的開發者,已經加入了React團隊。有一天他在推特上說,極度的模組化使得他非常難去構建一個統一的體驗。這句話指的就是,React生態圈整個棧的每一個部分來自不同開發者,想全部整合到一起的時就有很多零碎的問題。這是他剛開始做React現在的官方的CLI的時候發出的感慨,他在試圖整合社群的各種東西放到一個架子裡面,於是遇到了很多這樣的問題。我這裡完全沒有否認React生態圈繁榮的意思,我只是覺得可以有另外一種選擇,那就是我們可以做一個漸進式框架,這就是Vue選擇的方向。

三、漸進式框架Vue.js
1.Vue.js現狀
以下資料可以體現出Vue.js的現狀。

前一段時間突破了三萬星(如下圖所示),總下載量過百萬。

官網上每個月的使用者量為26萬,這個應該是不包含中國區資料。官方開發者外掛的周活躍使用者數在5萬5左右。這個資料是我覺得最有說服力的資料。安裝並且使用開發者外掛的Vue使用者,應該會在實際生產中真正頻繁使用Vue。

Google搜尋趨勢的相關資料如下圖所示。圖中,綠色的是Backbone的資料,黃色是Ember,紅色是React,藍色是Vue。可以看出React和Vue近兩年發展勢頭都比較迅猛。可以看出,Vue的曲線開始的是很早,2013年已經開始,但是有很長一段時間的增長是比較低的。因為在那一段時間我還在谷歌工作,Vue基本上是作為個人專案在運營。在過去一兩年中,Vue獲得了非常大的突破性發展。這個圖裡沒有Angular,因為Angular的量還是非常大的,如果放進去就破錶了。

這些資料並不能絕對地代表框架當前的熱度,但有一定的參考價值。可以看到React的勢頭很足。而由Vue的曲線還可以看出它的增長速度還在不停上揚。

  1. Vue的定位
    我在做Vue的過程中也在不停地思考它的定位,現在,我覺得它與其他框架的區別就是漸進式的想法,也就是“Progressive”——這個詞在英文中定義是漸進,一步一步,不是說你必須一竿子把所有的東西都用上。

  2. Vue的設計
    接下來我們回到之前看的圖:

Vue從設計角度來講,雖然能夠涵蓋這張圖上所有的東西,但是你並不需要一上手就把所有東西全用上,因為沒有必要。無論從學習角度,還是實際情況,這都是可選的。宣告式渲染和組建系統是Vue的核心庫所包含內容,而客戶端路由、狀態管理、構建工具都有專門解決方案。這些解決方案相互獨立,你可以在核心的基礎上任意選用其他的部件,不一定要全部整合在一起。

  1. Vue的實現
    接下來深入講一講這些具體的概念以及Vue在這些概念上具體是做怎樣的實現。

(1)宣告式渲染
現在基本所有的框架都已經認同這個看法——DOM應儘可能是一個函式式到狀態的對映。狀態即是唯一的真相,而DOM狀態只是資料狀態的一個對映。如下圖所示,所有的邏輯儘可能在狀態的層面去進行,當狀態改變的時候,View應該是在框架幫助下自動更新到合理的狀態,而不是說當你觀測到資料變化之後手動選擇一個元素,再命令式地去改動它的屬性。

下圖是Vue的一個模板示例,如果沒有用過Vue的話,可以大概感覺到這是一個怎樣的概念。

其實,在模板語法上,Vue跟Angular是比較相似。在Vue1.0裡面,模板實現跟Angular類似,如下圖所示,把模板直接做成在瀏覽器裡面parse成DOM樹,然後去遍歷這個樹,提取其中的各種繫結。

在Vue2.0中,渲染層的實現做了根本性改動,那就是引入了虛擬DOM。

從架構來講,Vue2.0 依然是寫一樣的模板,(Vue2.0於前段時間釋出,具體報道參見

http://t.cn/RVC0foZ)。在最左邊,Vue2.0跟1.0的模板語法絕大部分是相容的。Vue的編譯器在編譯模板之後,會把這些模板編譯成一個渲染函式。而函式被呼叫的時候就會渲染並且返回一個虛擬DOM的樹。這個樹非常輕量,它的職責就是描述當前介面所應處的狀態。當我們有了這個虛擬的樹之後,再交給一個patch函式,負責把這些虛擬DOM真正施加到真實的DOM上。在這個過程中,Vue有自身的響應式系統來偵測在渲染過程中所依賴到的資料來源。在渲染過程中,偵測到的資料來源之後,之後就可以精確感知資料來源的變動。到時候就可以根據需要重新進行渲染。當重新進行渲染之後,會生成一個新的樹,將新樹與舊樹進行對比,就可以最終得出應施加到真實DOM上的改動。最後再通過patch函式施加改動。

這樣做的主要原因是,在瀏覽器當中,JavaScript的運算在現代的引擎中非常快,但DOM本身是非常緩慢的東西。當你呼叫原生DOM API的時候,瀏覽器需要在JavaScript引擎的語境下去接觸原生的DOM的實現,這個過程有相當的效能損耗。所以,本質的考量是,要把耗費時間的操作儘量放在純粹的計算中去做,保證最後計算出來的需要實際接觸真實DOM的操作是最少的。

下面看渲染函式。用過React的開發者可能知道,React是沒有模板的,直接就是一個渲染函式,它中間返回的就是一個虛擬DOM樹。JSX實際就是一套用於讓我們更簡單地去描述樹狀結構的語法糖。

如下圖所示,在Vue2.0當中,可以看到就是說當比如左側的模板,經過Vue的編譯之後就會變成右側的東西。

這個函式類似於建立一個虛擬元素的函式,我們可以給它一個名字,給它描述應該有的屬性特性和可能其他的資料。然後後面這個最後這個引數是個陣列,包含了該虛擬元素的子元素。總的來說2.0的編譯器做的就是這個活。

同時,在Vue2.0裡,使用者可以選擇直接跳過模板這一層去手寫渲染函式,同時也有可選JSX支援。從開發者的偏好以及開發者的效益的角度來考量,模板和JSX是各有利弊的東西。模板更貼近我們的HTML,可以讓我們更直觀地思考語義結構,更好地結合CSS的書寫。JSX和直接渲染函式,因為是真正的JavaScript,擁有這個語言本身的所有的能力,可以進行復雜的邏輯判斷,進行選擇性的返回最終要返回的DOM結構,能夠實現一些在模板的語法限制下,很難做到的一些事情。

所以在Vue2.0裡,兩個都是可以選擇的。在絕大部分情況下使用模板,但是在需要複雜邏輯的情況下,使用渲染函式。在Vue2.0的路由和內部的一些實踐上,都大量地應用渲染函式做複雜的抽象元件,比如過渡動畫元件以及路由裡面的link元件,都是用渲染函式實現的,同時還保留了它本身的依賴追蹤系統。

如下圖所示,Vue的依賴追蹤通過ES5的 Object.defineProperty 方法實現。比如,我們給它一個原生物件,Vue會遍歷這個資料物件的屬性,然後進行屬性轉換。每一個屬性會被轉換為一個 getter 和一個 setter。同時每個元件會有一個對應的 watcher 物件,這個物件的職責就是在當前元件被渲染的時候,記錄資料上面的哪些屬性被用到了。

例如,在渲染函式裡面用到A.B的時候,這個就會觸發對應的 getter。整個渲染流程具體要點如下:

當某個資料屬性被用到時,觸發 getter,這個屬性就會被作為依賴被 watcher 記錄下來。

整個函式被渲染完的時候,每一個被用到的資料屬性都會被記錄。

相應的資料變動時,例如給它一個新的值,就會觸發 setter,通知資料物件對應資料有變化。

此時會通知對應的元件,其資料依賴有所改動,需要重新渲染。

對應的元件再次調動渲染函式,生成 Virtual DOM,實現 DOM 更新。

這樣一個流程跟主流的一些框架,例如React是有較大區別的。在React中,當元件複雜的時候需要用shouldComponentUpdate 做優化。但是,它也有自己的各種坑,比如要確保該元件下面的元件不依賴外部的狀態。雖說這在大部分情況下是夠用的,但遇到極大複雜度的應用,遇到效能瓶頸的時候,這個流程優化起來也是相當複雜的一個話題。

如下圖所示,在Vue裡面由於依賴追蹤系統的存在,當任意資料變動的時,Vue的每一個元件都精確地知道自己是否需要重繪,所以並不需要手動優化。用Vue渲染這些元件的時候,資料變了,對應的元件基本上去除了手動優化的必要性。

(2)元件系統
相信基本上所有的現代框架都已經走向了元件化道路,Web Components 從規範層面做這個實踐。主流框架都有各有不同的封裝,但核心思想都是一樣,把UI結構對映到恰當的元件樹,如下圖所示。

在Vue中,父子元件之間的通訊是通過 props 傳遞。從父向子單向傳遞;而如果子元件想要在父元件作用裡面產生副作用,就需要去派發事件。這樣就形成一個基本的父子通訊模式,在涉及大規模狀態管理的時候會有額外的方案,這個後面會提到。