2017前端框架何去何从
>這篇文章將從 AngularJS ReactJS Polymer 這幾個流行的框架入手,分析前端框架在這幾年發(fā)展中的關(guān)鍵技術(shù)點(diǎn),作為2015前端技術(shù)選型的參考。摘要:
- 初體驗
- 技術(shù)特點(diǎn)
- 組件化
- 應(yīng)用架構(gòu)
### 總結(jié)
**1. 初體驗**
拿TODO來作為引子好了.
? ? ? ? ?
Angular 的實(shí)現(xiàn)

React的實(shí)現(xiàn)(非flux架構(gòu))

Polymer的實(shí)現(xiàn)

三者共同對比

在Angular中有controller和component的概念是分離的,而react和polymer中只有component的概念。
實(shí)際上三者在最簡單的使用場景下差異并不大,Angular和polymer模板和代碼分離的方式更貼近于傳統(tǒng)的前端做法,而React寫法更像后端渲染。關(guān)于學(xué)習(xí)和使用成本的誰高誰低得問題沒有什么好爭論的,在MVVM已經(jīng)流行了這么久的情況下,三者入門門檻都差不多,但要用好都需要深入其中的運(yùn)行機(jī)制才行。
**2. 技術(shù)特點(diǎn)**
實(shí)際上所謂的MVVM框架的關(guān)鍵技術(shù)就一個:數(shù)據(jù)與視圖的綁定。在Angular/polymer/knockout/vue/avalon 中,這項技術(shù)的實(shí)現(xiàn)又可以拆分成兩個關(guān)鍵點(diǎn):模板分析和數(shù)據(jù)監(jiān)測。
模板分析的主要目的是對 {{title}} 這樣的標(biāo)記進(jìn)行收集。收集完成之后生成一個視圖更新函數(shù),在函數(shù)內(nèi)部保存著這個標(biāo)記所在的Dom片段和相關(guān)的數(shù)據(jù)名稱,函數(shù)被調(diào)用時會去重新取數(shù)據(jù)名稱對應(yīng)的數(shù)據(jù)(或者由外部將相應(yīng)的數(shù)據(jù)作為參數(shù)傳入),然后更新dom片段。這樣就實(shí)現(xiàn)了視圖的更新。一般框架會在啟動時就將模板分析完,生成相應(yīng)的視圖更新函數(shù)。當(dāng)數(shù)據(jù)更新的時候,就調(diào)用這些更新函數(shù)來更新視圖,那么問題來了,如何檢測數(shù)據(jù)的改動?
knockout/angular/avalon代表了三種方案:
- 使用自定義的數(shù)據(jù)對象及其指定的get和set函數(shù)。例如你只能使用 user.set("name","john")來給user對象的name屬性賦值,因為這樣它才能在set函數(shù)中知道修改了什么屬性,并且只調(diào)用相應(yīng)的視圖更新函數(shù)。這種方式不太爽的地方在于改變了原有的JS對象使用的方式。
- 使用 Object.defineProperty 的get和set函數(shù)來檢測對象屬性的改動,本質(zhì)上和上種沒有什么區(qū)別。但是它有一個缺陷,就是無法檢測新增的或刪除的屬性。有的框架是通過Object.observe來補(bǔ)充這種方案的,不過Object.observe 目前也只有chrome支持。這種方法改良了上面的開發(fā)體驗,你可以像使用原生JS對象一樣來操作你的數(shù)據(jù)。但是在實(shí)現(xiàn)上較為復(fù)雜。
- dirty check。這是angular正在使用的機(jī)制,它并不能像前兩種一樣一旦數(shù)據(jù)發(fā)生變化立即觸發(fā)更新回調(diào)。而是必須在調(diào)用了angular提供的一些方法,或者觸發(fā)了頁面上使用了ng-click等的元素上的事件后才會觸發(fā)。這些觸發(fā)時機(jī)是angular內(nèi)部就已經(jīng)實(shí)現(xiàn)了的,所以你幾乎感覺不到。這種方法被稱為"dirty"的原因是,它保存了所有屬性上一次的值,檢測是通過遍歷對象的所有屬性,對比它和上一次值是否一樣來實(shí)現(xiàn)的。如果是深層對象的話,它會層層遍歷。這種檢測方式結(jié)合了上面兩種的優(yōu)勢,但是對性能造成了負(fù)擔(dān)。
至此,兩個關(guān)鍵技術(shù)點(diǎn)都已講清楚,用一張圖來回顧一下

而在React中則相對簡單,React用的是類似于重繪的機(jī)制,當(dāng)觸發(fā)了 setState 之后,就完全重新渲染(并非立即觸發(fā),中間有類似于緩存的性能提升機(jī)制)。這看起來比起前面的方案簡單粗暴,但是卻因為virtual dom的實(shí)現(xiàn)化腐朽為神奇了。virtual dom指的是React內(nèi)部用來模擬真實(shí)dom的一種數(shù)據(jù)對象。當(dāng)重新渲染時,實(shí)際上是先生成這樣virtual dom,然后將其和上一次的virtual dom進(jìn)行對比,找出差異,最后由react在真實(shí)的dom上更新有差異的部分就夠了。因為virtual dom始終在內(nèi)存中,真實(shí)的dom操作非常少,而前面的幾種框架在更新視圖時常常會有大量的dom操作,因此react在性能上大大領(lǐng)先前一種類型的框架。同時也因為virtual dom仍然是標(biāo)準(zhǔn)的 js對象,所以使得"服務(wù)端渲染"也成為可能。值得注意的是,雖然React本身并不會像前面的框架一樣深入的去檢測數(shù)據(jù)的哪一部分發(fā)生了變化,但是可以通過官方提供的addon 和immutable.js來進(jìn)一步提高這一塊的性能。
(web前端學(xué)習(xí)交流群:328058344 禁止閑聊,非喜勿進(jìn)!)
**3. 組件化**
在組件化的方向上 react 和其他幾種框架幾乎已經(jīng)分道揚(yáng)鑣了。從 angular2.0的設(shè)計和新出的 aurelia 等框架中可以看到大家都在嘗試往 webcomponent 靠近。polymer號稱下個版本代碼將大幅減少,那無非是因為瀏覽器將實(shí)現(xiàn)標(biāo)準(zhǔn)了。靠近 webcomponent 的好處在于任何一個框架都將不再封閉,以 custom element作為接口層,能實(shí)現(xiàn)生態(tài)圈的融合。雖然 react 也有封裝成 custom element的方案,但是 react 并沒有很好的調(diào)用其他框架生成的 custom element 的方案。"像使用原生dom元素一樣使用custom element"的組件使用方式意味著尊重原生的dom使用方式,包括dom的事件等等。這和react"不操作真實(shí)dom"的基礎(chǔ)已經(jīng)方向相悖了。
react和其他框架的分歧其實(shí)目前看來并無優(yōu)劣之分,因為webcomponent目前除了chrome以外其他瀏覽器支持仍然不全面。另外考慮到特殊國情的話,大公司的產(chǎn)品仍然要面對IE8。不幸的是目前polymer的polyfill最低也只到IE9。而React能無痛支持IE8。再考慮到移動端的瀏覽器情況的話,也是使用react的技術(shù)阻力遠(yuǎn)小于webcomponent。
總體來看,webcomponent肯定會是趨勢,并且將促進(jìn)各個框架變得更加開放,更易互相融合。而react也仍將繼續(xù)依靠自己在實(shí)現(xiàn)上的優(yōu)勢繼續(xù)走下去。也許未來在這中間又將誕生新東西。
暫時拋開react和webcomponent。我們繼續(xù)深入兩個目前討論得很少但是卻很重要的問題(下面討論的組件問題都以封裝成custom element為基礎(chǔ)):
- 如何能把組件變得更易重用? 具體一點(diǎn):
- 我在用某個組件時需要重新調(diào)整一下組件里面元素的順序怎么辦?
- 我想要去掉組件里面某一個元素怎么辦?
- 如何把組件變得更易擴(kuò)展? 具體一點(diǎn):
- 業(yè)務(wù)方不斷要求給組件加功能怎么辦?
針對第一個問題,我所在的團(tuán)隊目前提出一個叫做"模板復(fù)寫"的規(guī)則,這個規(guī)則又分為"完全重寫"和"部分重寫"兩種規(guī)則:

?
**部分重寫**

這種方案已在angular中實(shí)現(xiàn)。并且在組件重用率高的系統(tǒng)中已經(jīng)驗證非常實(shí)用。但它也有缺陷,缺陷在于你必須知道當(dāng)前組件的實(shí)現(xiàn)方式和原有模板才能復(fù)寫。
第二個問題,可以用一種稱為"共享作用域"的方式來解決。例如上面的例子中story沒有顯示like數(shù)量,現(xiàn)在要顯示出來。常規(guī)方案有兩種:
- 改組件,在組件中增加這個功能。
- 給組件增加api用于獲取統(tǒng)計數(shù)據(jù),同時在統(tǒng)計數(shù)據(jù)發(fā)生變化時拋出事件通知外部。
第一種方案可能碰到的問題是當(dāng)再次發(fā)生變化,例如統(tǒng)計數(shù)據(jù)不要顯示在組件里面了。就得繼續(xù)改成第二種方案。 第二種方案可能碰到的問題是可能不斷有新的需求提出來,最后不得不把每一個內(nèi)部狀態(tài)都暴露出來,每一個操作過程都拋出事件。
"作用域共享"共享的方案是: 通過在一個特殊標(biāo)記 "import-to" 將某一段外部html引入到某個組件中去一起參與"模板解析"和"數(shù)據(jù)綁定",當(dāng)完成時再放回原來的位置。這樣這個外部html就能獲取到組件內(nèi)部任何狀態(tài)和數(shù)據(jù)了。這種方案看起來有點(diǎn)像hack,但其實(shí)只是換了一種方式來理解組件:組件分成兩個部分,一是數(shù)據(jù),二是視圖。視圖理論上應(yīng)該只受到它的邏輯是否足夠內(nèi)聚的約束,而不應(yīng)該受到它的子元素是否放在一起的約束。但是目前我們剛好使用了dom作為視圖的基礎(chǔ),所以視圖受到html結(jié)構(gòu)的約束,這個約束是不合理的。我們來用圖對比一下使用"作用域共享"前后的場景:

當(dāng)然,這種方案的缺陷仍然是你必須知道組件的具體實(shí)現(xiàn)。但這并不是一個不可克服的缺陷,我們看下aurelia的設(shè)計,它將template等等關(guān)鍵部分都設(shè)計成了可插拔的形式,這種結(jié)構(gòu)意味著未來有可能實(shí)現(xiàn)一種通用的模板語法來實(shí)現(xiàn)上述兩個功能。這樣就不再和底層耦合。
**4. 應(yīng)用架構(gòu)**
應(yīng)用架構(gòu)的范圍太廣,我們這里只討論那些已經(jīng)很好地組件化了的應(yīng)用,或者是沒組件化但是有明確層級劃分的應(yīng)用。我們以React 對應(yīng)的 FLUX 為切入點(diǎn)。?

我們再來結(jié)合facebook的官方FLUX代碼示例來看看每個部分:

facebook在介紹FLUX的時候的主要觀點(diǎn)是"MVC擴(kuò)展性不夠,FLUX可擴(kuò)展性高"。暫且不去討論FLUX與MVC的區(qū)別, 我們先來它是如何擴(kuò)展的,從上面的代碼中可以看到,ACTION不只是一個界面上的點(diǎn)擊事件所產(chǎn)生的,ajax請求、甚至一個初始化過程都可以產(chǎn)生動作,"動作"只是一個抽象。動作將傳遞給dispatcher,有dispatcher在去觸發(fā)store注冊的回調(diào)。你可能會想,從這個dispatcher實(shí)際上什么也沒干,這和我直接定義一個方法,觸發(fā)事件就直接調(diào)用這個方法有什么區(qū)別?區(qū)別在于,當(dāng)應(yīng)用增加功能、進(jìn)行擴(kuò)展時,應(yīng)用可能有多個部分要協(xié)同對同一個action進(jìn)行響應(yīng),并且不同的協(xié)同部分可能在執(zhí)行順序上有嚴(yán)格的先后之分。
> 舉個例子
如果我要對上面的TODO增加一個"統(tǒng)計區(qū)塊",如果是傳統(tǒng)的MVC寫法,你可能要新增一個statisticModel,然后在controller中的createTODO、deleteTODO中增加代碼來操作這個新的statisticModel。而FLUX不用修改已有的任何代碼,只需要寫新的store,并注冊一些回調(diào)到createAction、deleteAction中就夠了。所以可以看做是將MVC中的 "C主動操作M" 反轉(zhuǎn)成 "M來決定何時運(yùn)行"(當(dāng)然這種情況也就沒有C了), 但更好的是理解成是一種"事件系統(tǒng)"的變種。這就是它和MVC的區(qū)別。嚴(yán)格來說 FLUX 并不能算是facebook"發(fā)明"出來的,這樣的模型在很多事件驅(qū)動的后端框架中很常見,如[zero](https://github.com/sskyy/zero)、[yii](http://www.yiiframework.com/),只不過拿到前端來作為應(yīng)用架構(gòu)時比較新穎。
FLUX是目前高度推薦的應(yīng)用架構(gòu)方式,它并沒有強(qiáng)制使用的庫或者框架,所以并不局限于react,在angular、polymer中同樣能自由實(shí)現(xiàn)。特別是目前angular、polymer中的應(yīng)用開發(fā)并沒有一種應(yīng)用架構(gòu)的最佳實(shí)踐。angular中的模塊化既沒有異步加載也沒有作用域隔離的作用,實(shí)際使用時很雞肋。但是angular中的依賴注入、filter、service的設(shè)計非常全面,如果再能加上FLUX的架構(gòu)的話,威力不容小覷。對polymer來說情況更簡單,應(yīng)為polymer目前只考慮到element這一層,所以上層的應(yīng)用架構(gòu)可以自由實(shí)現(xiàn)。
值得補(bǔ)充的是,FLUX中的store,dispatcher可以更好地加強(qiáng)一下。store可以使用一些自動支持REST的庫來簡化開發(fā),dispatcher可以使用支持自定義順序等高級的事件代理實(shí)現(xiàn)。
**5. 總結(jié)**
2015將是前端框架相互借鑒相互融合的一年,隨著webcomponent的落地,大家都在像標(biāo)準(zhǔn)靠近。提前儲備這方面的技術(shù)肯定沒有問題。再深入到框架的技術(shù)細(xì)節(jié)中,我們看到在"渲染機(jī)制"、"數(shù)據(jù)綁定"、"組件化"、"模塊化"這些關(guān)鍵技術(shù)點(diǎn)中各個框架中都有非常精彩的實(shí)現(xiàn),值得深入學(xué)習(xí)。React異軍突起,也推薦持續(xù)關(guān)注,特別是在"應(yīng)用架構(gòu)"上,FLUX確實(shí)在整個業(yè)界起到了啟發(fā)的作用,相信會越來越流行,并且有越來越多實(shí)現(xiàn)方式。
轉(zhuǎn)載于:https://www.cnblogs.com/qianduantuanzhang/p/7735767.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的2017前端框架何去何从的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 推荐一些好的linux学习网站
- 下一篇: 吴恩达后,其钦点的百度研究院院长林元庆也