通往全棧工程師的捷徑 —— react

jopen 8年前發布 | 42K 次閱讀 前端技術

通往全棧工程師的捷徑 —— react

騰訊Bugly特約作者: 左明

首先,我們來看看 React 在世界范圍的熱度趨勢,下圖是關鍵詞“房價”和 “React” 在 Google Trends 上的搜索量對比,藍色的是 React,紅色的是房價,很明顯,人類對 React 的關注程度已經遠遠超過了對房價的關注。

從這些數據中,大家能看出什么?
可以很明顯的看出,我在一本正經的扯淡。

從2014年到現在,React、jQuery和 Angular 的熱度趨勢對比,可以很明顯的看到(上圖),React 在全球的熱度趨勢增長非常快。

上圖是 React 在國內的百度搜索指數,是拿 React 和 Nodejs 做了個對比,可以看出 React 的關注度也已經逼近 nodejs。

雖然在關注總量上 React 還遠不及 jQuery 和 Angular 等等,但它的增長幅度超乎你想象,你知道這意味著什么嗎?這意味著關注 React,你就比大多數人走在了業界的前沿!

那么React到底是什么鬼?

引用官網的簡介,”一個用來構建用戶界面的javascript庫”。

React 起源于 非死book 的內部項目,因為 FB 對市場上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設 Instagram 的網站。做出來以后,發現這套東西很好用,就在2013年5月開源了。

由于 React 的設計思想極其獨特,屬于革命性創新,性能出眾,代碼邏輯卻非常簡單。所以,越來越多的人開始關注和使用,認為它可能是將來 Web 開發的主流工具。

和 Backbone、Angular 等 MV* 框架不一樣,它只處理 View 邏輯 ,它只處理 View 邏輯,它只處理 View 邏輯。所以如果你喜歡它,你可以很容易的將它接入到現有工程中,然后用 React 重寫 HTML 部分即可,不用修改邏輯。

近幾年 web 領域的技術革新非常迅速,React也是一項新技術…話說React出來也已經2年了,其實并不算什么新技術了,只是在國內還沒有普及開,這篇文章的目的也是幫助大家更快的理解 react 和認識 react 能給我們帶來的價值。

React 這么火,那么它到底有什么牛逼的地方?

上圖是2015年年初的數據

這是 非死book 的好友動態頁面,也是 非死book 訪問量最大的頁面沒有之一,通過 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方法需要滿足這幾點:

  1. 只能通過 this.props 或 this.state 訪問數據
  2. 只能出現一個頂級組件
  3. 可以返回 null、false 或任何 React 組件
  4. 不能對 props、state 或 DOM 進行修改

需要注意的是,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我們還要做一些準備工作:

  1. 從后臺server或數據庫等來源拉取數據
  2. 調用React.renderToString()方法來生成HTML
  3. 最后發送HTML和數據給瀏覽器

代碼就不貼了,大家自行腦補。

需要注意的是這里的JSON字符串中可能出現</script>結尾標簽或HTML注釋,可能會導致語法錯誤,這里需要進行轉義。

頁面的示例代碼本來打算用大家更熟悉的HTML,但發現代碼量太多了,所以換成了jade代碼,沒用過jade的同學也順便了解一下,我也順便給jade打個廣告。 這個頁面做了這幾件事:

  1. 將前面在action里生成的HTML寫到#container元素里
  2. 引入必須的JS文件
  3. 獲取action提供的數據
  4. 渲染組件

這就是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 為例

  1. 啟動 xcode build
  2. 在模擬器中按下 Command + D 打開菜單,選擇 Debug in Chrome
  3. 在 Chrome dev tools 中調試

當然,react 并不是完美的,在實際使用時你也會發現她的一些缺點,比如:

(如果只是做安卓 app 開發,那么“蘋果兩件套+開發者證書”不是必須的,在windows下面開發即可。)

最后,大家在使用 react 開發時,可能會需要安裝React developer tools

最后是一點參考資料

書山有路勤為徑,react 便是那通往『全棧工程師』的捷徑。


騰訊Bugly簡介

Bugly是騰訊內部產品質量監控平臺的外發版本,其主要功能是App發布以后,對用戶側發生的Crash以及卡頓現象進行監控并上報,讓開發同學可以第一時間了解到App的質量情況,及時機型修改。目前騰訊內部所有的產品,均在使用其進行線上產品的崩潰監控。

來自: http://my.oschina.net/u/2533865/blog/535521

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!