標籤:
原文連結:http://mp.weixin.qq.com/s?__biz=MzA5Njc3Njk5NA==&mid=2650528748&idx=1&sn=4d3093e963ce76c642eb0c5d9a97625b#rd
React的起源
React來自於Facebook,是的,就是那個你們聽說過但是打不開的網站。Facebook的開發人員當時在開發一個廣告系統,因為對當前所有的MVC架構不滿意,所以就自己寫了一個UI架構,於是就有了React。後來因為覺得實在是好用,所以在2013年月份開源的此架構。經過這幾年的沉澱,React越來越強大,也受到了越來越多的開發人員喜愛。React目前(2015-05-04)的版本是0.14.0,從版本號碼上看還沒有達到1.0版,意味著React還在頻繁地修改,普遍應用於產品中還需要一定的時間。2015年三月份的F8開發人員大會上,Facebook又發布了React Native,正式把React的觸角伸向了APP。同時還為React native開發了一款基於Atom的IDE-Nuclide,也是開源。
React來勢洶洶,大有一統江湖的氣勢。前端開發人員應該保持學習新技術的熱情,很有必要熟悉React相關技術。下面我們簡要談談React相關的技術。
React的設計思想
熟悉一個新技術的關鍵是熟悉他的特色和理念
React架構本身和我們常用的JavaScript MVC架構,如:AngularJS,Backbone,Ember等,沒有直接的可比性。在React的官方部落格中明確闡述了React不是一個MVC架構,而是一個用於構建組件化UI的庫,是一個前端介面開發工具。所以頂多算是MVC中的V(view)。React並沒有重複造輪子,而是有很多顛覆性的創新,具體的特性如下:
編寫簡單直觀的代碼
在年初的React開發人員大會上,React的專案經理Tom Occhino講述了React的最大的價值,React最大的價值不是高效能的虛擬DOM、封裝的事件機制、伺服器端渲染,而是聲明式的直觀的編碼方式。React號稱能讓新人第一天開始使用就能開發新功能。簡單的編碼方式會讓新手能很快地上手,同時也降低了代碼維護的成本。這一特性決定了React能快速引起開發人員的興趣並廣泛傳播的基礎。以下是React基於這一理念的具體做法。
簡化可複用的組件
React構建UI是使用組件化的方式,而不是常見的模板。組件並不是一個新概念,它是某個獨立功能或者介面的封裝,達到複用或者UI和業務松耦合的目的。
組件化的設計理念也出現了很多年了,我們常用的ExtJS、YUI、jQueryUI、BootStrap等等都會提供大量的可複用的UI組件。比如在Bootstrap中使用對話方塊組件:
// 初始化$(‘#myModal‘).modal({ keyboard: false});// 顯示$(‘#myModal‘).modal(‘show‘);// 關閉事件$(‘#myModal‘).on(‘hidden.bs.modal‘, function (e) { // do something...});
可以看到我們常用的這些組件提供了大量的介面和配置,讓開發人員選擇合適的使用情境。這些組件的設計複雜,使用也較繁瑣,新人上手有一定的門檻。W3C也在加緊制定Web Components(即組件)的標準,試圖制定一個統一的簡單實用的標準化的組件概念。我們看看React是如何設計組件模型的以及其和Web Component的區別。
React架構裡面使用了簡化的組件模型,但更徹底地使用了組件化的概念。React將整個UI上的每一個功能模組定義成組件,然後將小的組件通過組合或者嵌套的方式構成更大的組件。這種做法已經在instagram網站上普遍實施,大家可以看看instagram的前端原始碼。 如下通過一個簡單的例子來闡述React中模組化的概念。這個例子來自於React的官方網站教程,是完成一個簡單的評論框。這個評論框主要包含兩個部分,評論列表和評論表單。顯示效果如下:
按照React模組組合的設計,把評論框組件commentBox分為兩個子組件模組:commentList和commentForm,代碼如下:
<div className="commentBox"> <h1>Comments</h1> <CommentList data={this.state.data} /> <CommentForm onCommentSubmit={this.handleCommentSubmit} /></div>
commentList和commentForm組件對應的代碼如下:
<div className="commentList"> {commentNodes}</div><form className="commentForm" onSubmit={this.handleSubmit}> <input type="text" placeholder="Your name" ref="author" /> <input type="text" placeholder="Say something..." ref="text" /> <input type="submit" value="Post" /></form>
從代碼中可以看到CommentList組件又可以劃分為comment組件的列表。comment組件代碼如下:
<div className="comment"> <h2 className="commentAuthor"> {this.props.author} </h2> <span dangerouslySetInnerHTML= /></div>
可以看出,為了完成評論框功能,使用React定義了四個不同的組件:commentBox、commentList、commentForm、comment。commentBox是由commentList和commentForm組合而來,commentList是由comment組合而來。這個例子充分體現了React組件化的理念,每個組件的UI和邏輯都定義到了內部,暴露少量的API和外部互動,組件之間組合形成更複雜的組件。總結一下,React的組件具有如下的特性:
React使用了組件化的設計,所以開發人員自然而然和原生的Web Components相提並論,StackExchange上有一篇精彩的討論,解釋了React的組件和原生組件的對比。文章從語言層面、樣式的封裝、資料繫結、DOM操作方式等這幾個方面展開討論,結論是說兩者沒有優劣之分,只是編碼習慣問題。Web Components規範畢竟還在制定過程中,應該可以從React的組件設計方式上借鑒一些理念。在後續的文章中,會深入探討React中組件的設計原理及使用。
虛擬DOM
在JavaScript中DOM操作是獨立成為一個分支的。各瀏覽器在實現DOM操作庫也是大同小異,都是在單獨的模組中實現了DOM操作,由於各種技術上的原因,DOM操作的效能損耗相對於其他動作是很大的。在前端開發中都是需要特別盡量保持較小的DOM操作次數來提高效能。
React作為一個UI架構,不可避免要有介面上元素的互動。為了提高效能,React在操作頁面互動時引入了虛擬DOM的概念。虛擬DOM是在React中用JavaScript重新實現的一個DOM模型,和原生的DOM並沒有多少關係,只是借鑒了原生DOM的一些概念。虛擬DOM並沒有完全實現DOM,只是保留了元素直接的層級關係和少量必要的屬性。因為減少了不必要的複雜性,實踐校正的結果是虛擬DOM的效能比原生DOM高很多。來看看普通DOM和虛擬DOM在代碼上的差別。
如下是使用原生DOM產生的元素:
var a = document.createElement(‘a‘)a.setAttribute(‘class‘, ‘link‘)a.setAttribute(‘href‘, ‘https://github.com/facebook/react‘)a.appendChild(document.createTextNode(‘React‘))
那麼使用虛擬DOM則代碼為如下:
var a = React.createElement(‘a‘, { className: ‘link‘, href: ‘https://github.com/facebook/react‘}, ‘React‘)
可以看到React中使用了自己實現的createElement
方法來產生元素DOM結構。
基於React開發中構建的DOM都是通過虛擬DOM進行的。在React的實際的使用中,需要根據不同的資料展現不同的UI,當資料變化時,React會重新構建整個DOM樹,然後將當前的DOM樹和之前的比較,得到DOM樹的區別,然後僅僅把變化的部分反映到實際的瀏覽器UI更新上。React會在同一個事件迴圈內合并DOM的變化,只是會對比開始和結束的DOM變化,忽略中間過程的DOM變化。儘管每次資料變化都是重新構建DOM樹,但虛擬DOM的操作效能極高。這樣使用React時,開發人員不在需要關心資料變化時頁面上DOM元素的更新,而只是關心各個資料狀態下頁面實際展現的效果。此外,因為React使用了由JavaScript實現的虛擬DOM,意味著可以在伺服器端完成HTML結構的構建。
JSX
JSX是React的重要組成部分,他使用類似XML標記的方式來聲明介面及關係,所以他只是一個文檔規範。如下是一個在React裡面使用JSX的例子:
var HelloMessage = React.createClass({ render: function() { return <div>Hello {this.props.name}</div>; }});React.render(<HelloMessage name="John" />, mountNode);
可以看到如上使用了JSX的代碼,像是HTML和JavaScript代碼的混合體。很多人很不習慣這樣的編碼方式,認為這和我們一直倡導的表現和邏輯分離的思想相違背,是一種倒退。那麼React這樣的設計用意是啥呢?
React一個主要的設計理念是編寫簡單容易理解的代碼。HTML模板的作用是讓表現和邏輯分離,但是很多情況下模板還是嚴重依賴於商務邏輯,兩者沒有辦法做到完全的松耦合。稍微複雜一點的例子,比如AngularJS使用了一套獨特的機制來讓UI和邏輯互動,範例程式碼如下。
<ul class="unstyled"> <li ng-repeat="todo in todoList.todos"> <input type="checkbox" ng-model="todo.done"> <span class="done-"></span> </li></ul>
使用AngularJS的確從代碼角度做到表現和邏輯分離,但是在HTML裡面混入了大量的屬性標記,這些標記但從語義上很難理解,新手比如要整個熟悉Angular中每個類似ng-*對應的用法及意義才能理解整個邏輯,所以有一定的入門門檻。如上例子使用JSX方式編寫如下:
render: function () { var lis = this.todoList.todos.map(function (todo) { return ( <li> <input type="checkbox" checked={todo.done}> <span className="done-{todo.done}">{todo.text}</span> </li>); }); return ( <ul class="unstyled"> {lis} </ul> );}
可以看到,JSX中除了使用HTML標記之外,並沒有複雜的標記。這種自然而直觀的方式直接降低了React的學習門檻並且讓代碼更容易理解。
JSX只是簡化了React的使用難度,但並不是必須的。在React中也可以不使用JSX,而是使用原生JavaScript的方式編寫代碼。在實際使用過程中也是把JSX轉換成了JavaScript代碼來啟動並執行。React官方網站上提供了一個線上轉換JSX到原生JavaScript代碼的工具,通過這個工具也可以體會JSX使用上的優勢及其內在原理。
Flux
Flux是另外一個獨立於React的架構。之所以說Flux是一個架構而不是架構或者類庫,是因為Flux僅僅用於配合React架構來處理組件和資料之間的互動。簡單來說Flux就是用於管理資料流。和其他MVC架構倡導的雙向資料繫結不同,Flux使用了單向資料繫結的機制,即資料模型到視圖的流動。如下兩個圖展示MVC和Flux之間的差異:
Flux中主要使用了三個概念:Dispatcher、Action和Store。這三個概念區別於MVC的model、view和controller概念,因為MVC中更多的是資料雙向繫結。
Actions是用於傳遞資料給Dispatcher的操作集合。Action可能來自於使用者介面的操作,也可能是伺服器端的資料更新。
Dispatcher是一個全域的分發器,接受Action,並傳遞給註冊的回呼函數。
Stores包含了應用的狀態及註冊到Dispatcher的回呼函數,這些函數用於處理商務邏輯。
和React Views最密切的是Store,React view從Store取得state和其他資料,並更新介面。
總結
從以上的React相關設計可以看出,React是以降低前端開發的複雜度為原則的。使用React編寫的代碼也易於理解,所以適合大規模多人開發,能提高項目的開發效率和品質。
參考連結
深入淺出React(一):React的設計哲學 - 簡單之美
Why did we build React?
Facebook React 和 Web Components(Polymer)對比優勢和劣勢
Getting Started with Facebook’s React.js
React架構為什麼這麼火:React的設計思想