騰訊Bugly特約做者: 左明javascript
首先,咱們來看看 React 在世界範圍的熱度趨勢,下圖是關鍵詞「房價」和 「React」 在 Google Trends 上的搜索量對比,藍色的是 React,紅色的是房價,很明顯,人類對 React 的關注程度已經遠遠超過了對房價的關注。html
從這些數據中,你們能看出什麼?
能夠很明顯的看出,我在一本正經的扯淡。前端
從2014年到如今,React、jQuery和 Angular 的熱度趨勢對比,能夠很明顯的看到(上圖),React 在全球的熱度趨勢增加很是快。java
上圖是 React 在國內的百度搜索指數,是拿 React 和 Nodejs 作了個對比,能夠看出 React 的關注度也已經逼近 nodejs。node
雖然在關注總量上 React 還遠不及 jQuery 和 Angular 等等,但它的增加幅度超乎你想象,你知道這意味着什麼嗎?這意味着關注 React,你就比大多數人走在了業界的前沿!react
那麼React究竟是什麼鬼?git
引用官網的簡介,」一個用來構建用戶界面的javascript庫」。程序員
React 起源於 Facebook 的內部項目,由於 FB 對市場上全部 JavaScript MVC 框架,都不滿意,就決定本身寫一套,用來架設 Instagram 的網站。作出來之後,發現這套東西很好用,就在2013年5月開源了。github
因爲 React 的設計思想極其獨特,屬於革命性創新,性能出衆,代碼邏輯卻很是簡單。因此,愈來愈多的人開始關注和使用,認爲它多是未來 Web 開發的主流工具。web
和 Backbone、Angular 等 MV* 框架不同,它只處理 View 邏輯 ,它只處理 View 邏輯,它只處理 View 邏輯。因此若是你喜歡它,你能夠很容易的將它接入到現有工程中,而後用 React 重寫 HTML 部分便可,不用修改邏輯。
近幾年 web 領域的技術革新很是迅速,React也是一項新技術…話說React出來也已經2年了,其實並不算什麼新技術了,只是在國內尚未普及開,這篇文章的目的也是幫助你們更快的理解 react 和認識 react 能給咱們帶來的價值。
React 這麼火,那麼它到底有什麼牛逼的地方?
上圖是2015年年初的數據
這是 Facebook 的好友動態頁面,也是 Facebook 訪問量最大的頁面沒有之一,經過 Chrome React 插件能夠看到這個頁面確實是用 React 實現的。 (有同事問我爲何關注柳巖,我說由於我是柳巖的球迷啊)
前面給你們來了一波前戲,相信你們已經有點火燒眉毛了,那麼,進入正題: 首先,先跟你們描述下 React 最特別的部分,聽完這部分你們基本就可以在腦海裏創建起一個 React 的大體印象。
而後是 React 的核心內容,聽完這部分你們待會回去就能夠開始寫代碼而後今天晚上發佈上線了。 最後是 React 可以給咱們實際帶來的價值,咱們不做無心義的代碼重構。
首先,咱們來看JSX——
JSX 使用的是預編譯模板,React 提供了一個預編譯工具,叫 react-tools,能夠經過 npm 命令安裝,通常是在開發時期運行,運行後它會啓動一個監聽程序,實時監聽 JSX 源碼的修改,而後自動編譯爲 JS 代碼。
你們留意下,render() 方法裏的被編譯成了 React.createElement(),它這麼作,目的就是爲了實現虛擬 DOM。
接下來咱們來了解 React 最大的亮點 ———— 虛擬 DOM。
傳統 web app 和 DOM 直接交互,由App來控制DOM的構建和渲染、元素屬性的讀寫、事件的註冊和銷燬等等。 當新產品剛上線的時候,這種作法看起來也挺好。但隨着產品功能愈來愈豐富、代碼量愈來愈多、開發團隊人員愈來愈多 —————
一年後
你的代碼可能會變成這樣。
固然,合理的代碼規劃可以避免這類問題,但團隊裏不免會有擅長屠宰式編程的同窗,分分鐘把你代碼改的面目全非。
這時,React的虛擬DOM和單項數據流就能很好的解決這個問題。
虛擬DOM則是在DOM的基礎上創建了一個抽象層,咱們對數據和狀態所作的任何改動,都會被自動且高效的同步到虛擬DOM,最後再批量同步到DOM中。
虛擬DOM會使得App只關心數據和組件的執行結果,中間產生的DOM操做不須要App干預,並且經過虛擬DOM來生成DOM,會有一項很是可觀收益——-性能。
全部人都知道DOM慢,渲染一個空的DIV,瀏覽器須要爲這個DIV生成幾百個屬性,而虛擬DOM只有6個,因此減小沒必要要的重排重繪以及DOM讀寫可以對頁面渲染性能有大幅提高。
那麼咱們來看看虛擬DOM是怎麼作的:React會在內存中維護一個虛擬DOM樹,當咱們對這個樹進行讀或寫的時候,其實是對虛擬DOM進行的。當數據變化時,而後React會自動更新虛擬DOM,而後拿新的虛擬DOM和舊的虛擬DOM進行對比,找到有變動的部分,得出一個Patch,而後將這個Patch放到一個隊列裏,最終批量更新這些Patch到DOM中。
這樣的機制能夠保證即使是根節點數據的變化,最終表如今DOM上的修改也只是受這個數據影響的部分,能夠保證很是高效的渲染。
但這樣也是有必定的缺陷的——首次渲染大量DOM時由於多了一層虛擬DOM的計算,會比innerHTML插入方式慢,因此使用時須要確保不要一次性渲染大量DOM。
幾個UI組件的渲染性能對比
http://mathieuancelin.github.io/js-repaint-perfs/
一個最基本的 React 組件由數據和JSX兩個主要部分構成,咱們先來看看數據。
這是一個簡單單完整的React組件【類】,細節你們先不用太在乎細節,瞭解機制就能夠。
props 主要做用是提供數據來源,能夠簡單的理解爲 props 就是構造函數的參數。 state 惟一的做用是控制組件的表現,用來存放會隨着交互變化狀態,好比開關狀態等。
JSX 作的事情就是根據 state 和 props 中的值,結合一些視圖層面的邏輯,輸出對應的 DOM 結構。
前面咱們知道了一個簡單的組件的構成,但單個的組件確定不能知足實際需求,咱們須要作的是將這些獨立的組件進行組裝,同時找出共性的部分進行復用。
好比這樣一個場景
咱們以這樣一個界面爲例,能夠看出,裏面的 <Pub/> <Article/> 都是最細粒度的組件,是能夠複用的。 首先,咱們來看下Article的代碼——
這個就是咱們分解出來的 Article 組件,它須要2個屬性,article對象和showImage。article對象包含圖片、地址、標題、描述信息,showImage是一個布爾類型,用來判斷是否須要顯示成一個圖片。
這個組件自己的實現能夠很簡單也能夠很複雜,但使用者可不關心你的內部實現,使用者關心的是組件須要什麼參數就能夠了。
外國人的組件化思想比咱們國內的普及程度高不少,不僅侷限於軟件開發,包括實體行業的咖啡機、加油站、 兒童搖搖車都有這種設計思想在裏面。
但願你們在設計模塊的時候,也儘量將組件邏輯對外透明,來減小維護成本。
你們留意一下標虛線的部分,這裏複用了 Article 組件。這時的 Article 組件看起來就是一個普通的標籤而已,簡單吧。
這個是熱問組件,也複用了 Article 組件。這就是 React 如絲般順滑的組件複合。
這個,叫作竹筧,是中日傳統禪文化中常見的庭院裝飾品,它的構造可簡單可複雜,但原理很簡單,好比這個竹筧,水從竹筧頂部入口流入內部,並按照固定的順序從上向下依次流入各個小竹筒,而後驅動水輪轉動。對於強迫症患者來講,觀賞竹筧的絕對是一種很享受的過程的最愛,你會發現這些小玩意居然能這麼流暢的協調起來,好神奇。
若是竹筧是一個組件的話,那麼水就是組件的數據流。
在React中,數據流是自上而下單向的從父節點傳遞到子節點,因此組件是簡單且容易把握的,他們只須要從父節點提供的props中獲取數據並渲染便可。若是頂層組件的某個prop改變了,React會遞歸地向下遍歷整棵組件數,從新渲染全部使用這個屬性的組件。
這個是前面看到的 Article 題組件,擁有一個叫作 articles 的屬性。
在組件內部,能夠經過this.props來訪問props,props是組件惟一的數據來源,對於組件來講:
props永遠是隻讀的。
組件的屬性類型若是不進行聲明和驗證,那麼極可能使用者傳給你的屬性值或者類型是無效的,那會致使一些意料以外的故障。好在React已經爲咱們提供了一套很是簡單好用的屬性校驗機制——
React有一個PropTypes屬性校驗工具,通過簡單的配置便可。當使用者傳入的參數不知足校驗規則時,React會給出很是詳細的警告,定位問題不要太容易。
PropTypes包含的校驗類型包括基本類型、數組、對象、實例、枚舉——
以及對象類型的深刻驗證等等。若是內置的驗證類型不知足需求,還能夠經過自定義規則來驗證。 若是某個屬性是必須的,在類型後面加上 .isRequired 便可。
React的一大創新,就是把每個組件都當作是一個狀態機,組件內部經過state來維護組件狀態的變化,這也是state惟一的做用。
state通常和事件一塊兒使用,咱們先看state,而後看看state和事件怎樣結合。
這是一個簡單的開關組件,開關狀態會以文字的形式表如今按鈕的文本上。
首先看render方法,返回了一個button元素,給button註冊了一個事件用來處理點擊事件,在點擊事件中對state的on字段取反,並執行 this.setState() 方法設置on字段的新值。一個開關組件就完成了。
組件渲染完成後,必須有UI事件的支持才能正常工做。
React經過將事件處理器綁定到組件上來處理事件。
React事件本質上和原生JS同樣,鼠標事件用來處理點擊操做,表單事件用於表單元素變化等,Rreact事件的命名、行爲和原生JS差很少,不同的地方是React事件名區分大小寫。
好比這段代碼中,Article組件的section節點註冊了一個onClick事件,點擊後彈出alert。
有時候,事件的處理器須要由組件的使用者來提供,這時能夠經過props將事件處理器傳進來。
這個是剛纔那個Article組件的使用者,它提供給Article組件的props中包含了一個onClick屬性,這個onClick指向這個組件自身的一個事件處理器,這樣就實現了在組件外部處理事件回調。
這是一個React組件實現組件可交互所需的流程,render()輸出虛擬DOM,虛擬DOM轉爲DOM,再在DOM上註冊事件,事件觸發setState()修改數據,在每次調用setState方法時,React會自動執行render方法來更新虛擬DOM,若是組件已經被渲染,那麼還會更新到DOM中去。
這些是React目前支持的事件列表。
React的組件擁有一套清晰完整並且很是容易理解的生命週期機制,大致能夠分爲三個過程:初始化、更新和銷燬,在組件生命週期中,隨着組件的props或者state發生改變,它的虛擬DOM和DOM表現也將有相應的變化。
首先是初始化過程,這裏會着重講,須要充分理解。
組件類在聲明時,會先調用 getDefaultProps() 方法來獲取默認props值,這個方法會且只會在聲明組件類時調用一次,這一點須要注意,它返回的默認props由全部實例共享。
在組件被實例化以前,會先調用一次實例方法 getInitialState() 方法,用於獲取這個組件的初始state。
實例化以後就是渲染,componentWillMount方法會在生成虛擬DOM以前被調用,你能夠在這裏對組件的渲染作一些準備工做,好比計算目標容器尺寸而後修改組件自身的尺寸以適應目標容器等等。
接下來就是渲染工做,在這裏你會建立一個虛擬DOM用來表示組件的結構。對於一個組件來講,render 是惟一一個必須的方法。render方法須要知足這幾點:
須要注意的是,render 方法返回的是虛擬DOM。
渲染完成之後,咱們可能須要對DOM作一些操做,好比截屏、上報日誌、或者初始化iScroll等第三方非React插件,能夠在 componentDidMount() 方法中作這些事情。固然,你也能夠在這個方法裏經過 this.getDOMNode() 方法取得最終生成DOM節點,而後對DOM節點作愛作的事情,但須要注意作好安全措施,不要緩存已經生成的DOM節點,由於這些DOM節點隨時可能被替換掉,因此應該在每次用的時候去讀取。
組件被初始化完成後,它的狀態會隨着用戶的操做、時間的推移、數據更新而產生變化,變化的過程是組件聲明週期的另外一部分 ——
更新過程。
當組件已經被實例化後,使用者調用 setProps() 方法修改組件的數據時,組件的 componentWillReceiveProps() 方法會被調用,在這裏,你能夠對外部傳入的數據進行一些預處理,好比從props中讀取數據寫入state。
默認狀況下,組件在 setState() 以後,React會遍歷這個組件的全部子組件,進行「灌水」,將props從上到下一層一層傳下去,並逐個執行更新操做,雖然React內部已經進行過不少的優化,這個過程並不會花費多少時間,可是程序員裏永遠不缺少長期性能飢渴的同窗,不用擔憂,React有一個可以解決你性能飢渴的辦法——shouldComponentUpdate()——有時候,props發生了變化,但組件和子組件並不會由於這個props的變化而發生變化,打個比方,你有一個表單組件,你想要修改表單的name,同時你可以確信這個name不會對組件的渲染產生任何影響,那麼你能夠直接在這個方法裏return false來終止後續行爲。這樣就可以避免無效的虛擬DOM對比了,對性能會有明顯提高。
若是這個時候有同窗仍然飢渴難耐,那麼你能夠嘗試不可變數據結構(用過mongodb的同窗應該懂)。
組件在更新前,React會執行componentWillUpdate() 方法,這個方法相似於前面看到的 componentWillMount()方法,惟一不一樣的地方只是這個方法在執行的時候組件是已經渲染過的。須要注意的是,不能夠在這個方法中修改props或state,若是要修改,應當在 componentWillReceiveProps() 中修改。
而後是渲染,React會拿此次返回的虛擬DOM和緩存中的虛擬DOM進行對比,找出【最小修改點】,而後替換。
更新完成後,React會調用組件的componentDidUpdate 方法,這個方法相似於前面 componentDidMount 方法,你仍然能夠在這裏能夠經過 this.getDOMNode() 方法取得最終的DOM節點。
香港電影結尾常常看到一個劇情,就是英雄戰勝了壞人,而後警察出來擦屁股——
componentWillUnmount 除了擦屁股什麼也作不了。
你能夠在這個方法中銷燬非React組件註冊的事件、插入的節點,或者一些定時器之類。這個過程可能容易出錯,好比綁定了事件卻沒銷燬,這個React可幫不了你,你本身約的炮,含着淚也要打完。
下面咱們來看看React怎樣結合nodejs實現服務端渲染。
服務端渲染有多快我就很少說了。
由於有虛擬DOM的存在,React能夠很容易的將虛擬DOM轉換爲字符串,這便使咱們能夠只寫一份UI代碼,同時運行在node裏和和瀏覽器裏。
var html = React.renderToString(elem);
在node裏將react組件HTML渲染爲HTML,一句代碼便可。
不過圍繞這個renderToString咱們還要作一些準備工做:
代碼就不貼了,你們自行腦補。
須要注意的是這裏的JSON字符串中可能出現</script>結尾標籤或HTML註釋,可能會致使語法錯誤,這裏須要進行轉義。
頁面的示例代碼原本打算用你們更熟悉的HTML,但發現代碼量太多了,因此換成了jade代碼,沒用過jade的同窗也順便了解一下,我也順便給jade打個廣告。 這個頁面作了這幾件事:
這就是React的服務端渲染,組件的代碼先後端均可以複用。
是否是感受React挺牛逼的?還沒完!
React可以用一套代碼同時運行在瀏覽器和node裏,並且可以以原生App的姿式運行在iOS和Android系統中,即擁有了web迭代迅速的特性,又擁有原生App的體驗。
這個姿式叫作 React-Native。
這是React和React-Native在github上的數據,能夠看出React-Native也是至關熱門——由於React-Native可以使React的價值最大化,這個價值是什麼呢——對業務來講,意味着不須要爲了作終端版本就招聘和前端等量人力的終端開發,同時意味着咱們成爲全棧工程師有了一個捷徑。
瞭解iOS開發的同窗都知道,水果公司對應用上架的審覈效率實在讓人無力吐槽,不少團隊上一個版本還沒審覈結束,下一個版本就已經作好了。而React-Native支持從網絡拉取JS,這樣iOS應用也可以像web同樣實現快速迭代了。
上圖就是react-native的調試過程,以 iOS 爲例
固然,react 並非完美的,在實際使用時你也會發現她的一些缺點,好比:
(若是隻是作安卓 app 開發,那麼「蘋果兩件套+開發者證書」不是必須的,在windows下面開發便可。)
最後,你們在使用 react 開發時,可能會須要安裝React developer tools
最後是一點參考資料
書山有路勤爲徑,react 即是那通往『全棧工程師』的捷徑。
Bugly是騰訊內部產品質量監控平臺的外發版本,其主要功能是App發佈之後,對用戶側發生的Crash以及卡頓現象進行監控並上報,讓開發同窗能夠第一時間瞭解到App的質量狀況,及時機型修改。目前騰訊內部全部的產品,均在使用其進行線上產品的崩潰監控。