原文: 5 common practices that you can stop doing in React
從下面這點來講,很難說React
是這個星球上最受歡迎的庫之一。不少人對React
感興趣,新的開發者之因此傾向於使用這個框架的緣由是由於它的UI優先方法。雖然這些年React
和它的整個生態已經比較成熟,可是在某些狀況下你仍然會這樣問本身:「正確的作法究竟是什麼?」javascript
這是一個好問題--並不老是有一個通用的「正確」的作事方式。事實上,正如你所知,最佳實踐並不老是那麼的適用。長遠來看,其中的某些寫法可能會影響性能,可讀性,並致使效率低下。html
在本文中,我將描述在React
開發中5種被廣泛接受的寫法,可是實際上你是能夠避免這樣作的。固然,我將解釋爲何我認爲這些作法是能夠避免的,而且我會建議你用其餘的寫法來完成相同的事情。前端
React
的開發人員在如何使React
更快投入了大量的精力,每次迭代新的優化方法又會被添加進去。在我看來,你不該該花費時間優化這些東西,除非你發現了真正的性能影響。java
爲何?react
React
相比其它的前端平臺更容易擴展,由於你沒必要重寫某個模塊來加快應用的速度。致使性能問題的罪魁禍首一般是React
使用更新虛擬DOM
的reconciliation
過程。webpack
讓咱們看一下React
是如何處理下面的事情的。在每一個render()
中,React
生成了一個由UI元素組成的樹——子節點是實際的DOM
元素。當state
或者props
更新的時候,React須要使用最小的改變次數來生成一個新的樹,並保持可預測性。git
想象一下,你看到的樹是這個樣子的:github
想象一下,你的應用接收到新的數據,下列的節點須要被更新:web
React
一般會從新渲染整個子樹,而不是像這樣僅僅渲染相關節點:ajax
當頂層組件的state
發生改變的時候,它的子元素都會從新渲染。這是默認的行爲,在小型應用中不須要擔憂。隨着應用的逐漸擴展,您應該考慮使用Chrome Profiling
工具來測量實際性能。這個工具將爲你提供在沒必要要的渲染上所浪費時間的精確數據。若是這些數據很重要,你能夠經過向你的組件中添加shouldComponentUpdate
鉤子來優化你的渲染時間。
這個鉤子在從新渲染開始以前將被觸發,默認返回true
:
shouldComponentUpdate(nextProps, nextState) { return true; }
當返回true
時,React
的diff
算法接管並從新渲染整個子樹。你能避免這樣的狀況發生,經過在shouldComponentUpdate
中添加比較邏輯,而且僅當相關的props
發生改變的時候更新邏輯。
shouldComponentUpdate(nextProps, nextState) { if (this.props.color !== nextProps.color) { return true; } if (this.state.count !== nextState.count) { return true; } return false; }
除了color/count,其它任何props/state
改變,這個組件都不會更新。
除此以外,還有一些開發人員一般忽略的non-React
優化技巧,但它們會影響應用程序的性能。
我將在下面列舉一些能夠避免的習慣和解決方案:
Cloudinary
來優化react
圖片由於它有它本身的react
庫,可是你也可使用Amazon S3
或者Firebase
。Gzipping
構建文件(bundle.js)能夠有效的減小文件大小。你須要修改webserver
的配置文件。Webpack
有一個gzip
壓縮插件,叫作compression-webpack-plugin
。你可使用這個技術在構建時間生成bundle.js.gz
。雖然單頁面應用很是棒,可是他們仍然存在兩個問題。
JavaScript
緩存。若是應用很大,首次加載應用的時間將會很慢。web
爬蟲程序將沒法索引JavaScript
生成的內容。搜索引擎將看到你的應用是空白的,而後排名不好。這就是服務端渲染技術派上用處的地方。在SSR
中,JavaScript
內容最初是由服務器渲染的。在首次渲染後,客戶端的腳本接管,並像一個正常的SPA
應用運行。因爲你須要使用Node/Express
服務,所以在以傳統SSR
進行構建時複雜度和花費更高。
若是你是爲了搜索引擎優化、谷歌索引和沒有任何問題地抓取JavaScript內容,那麼有一個好消息。谷歌其實是在2016年開始抓取JavaScript素材的,這個算法如今已經能夠完美地工做了。
如下是2015年10月Webmaster博客的節選。
今天,只要你不阻止Googlebot抓取你的JavaScript或CSS文件,咱們就能 像現代瀏覽器同樣渲染和理解你的網頁。爲了反映這一改進,咱們最近更新了咱們的 技術站長指南,建議不要禁止Googlebot抓取你網站的CSS或JS文件。
若是你使用服務器端渲染,是由於你擔憂你的谷歌頁面排名,那麼你不須要使用SSR
。它曾經是過去的事,但如今不是了。
然而,若是你正在改善首次渲染速度,那麼你應該嘗試使用像Next.js
這樣的庫來實現更加簡單的SSR
。Next
將節省你在設置Node/Express
服務所花費的時間。
在使用React
作開發時,我曾親自嘗試過多種不一樣的樣式理念,爲了能找到一種新的引入樣式到React
組件的方式。在React組件中使用已經存在多年的傳統的CSS-in-CSS
方法。你的樣式表所有樣放在一個樣式表目錄,而後你能夠將所須要的CSS
導入到你的組件中。
然而,當你使用這些組件的時候,樣式表再也不清晰明瞭。React
鼓勵你以組件化的方式來思考你的應用,然而樣式表則強迫你以文檔的角度來思考。
目前有多種方法將CSS和JS代碼合併到一個文件中。內聯樣式多是其中最流行的。
const divStyle = { margin: '40px', border: '5px solid pink' }; const pStyle = { fontSize: '15px', textAlign: 'center' }; const TextBox = () => ( <div style={divStyle}> <p style={pStyle}>Yeah!</p> </div> ); export default TextBox;
你不須要再導入CSS
,可是這會犧牲可讀性和可維護性。除了這樣作,內聯樣式不支持媒體查詢,僞元素以及樣式回退。固然,有一些技巧可讓你解決上述問題,可是使用起來卻並不方便。
這就是CSS-in-JSS
派上用處的地方,內聯樣式並不徹底是CSS-in-JSS
。下面的代碼演示了使用styled-components
的概念。
import styled from 'styled-components'; const Text = styled.div` color: white, background: black ` <Text>This is CSS-in-JS</Text>
在瀏覽器中看上去是這樣的:
<style> .hash234dd2 { background-color: black; color: white; } </style> <p class="hash234dd3">This is CSS-in-JS</p>
新的<style>
標籤被添加到了頂層的DOM
中,不像內聯樣式,實際的CSS樣式在這裏生成。因此,任何能在CSS
運行的東西,在樣式組件中也能工做。此外,這個技術還加強了CSS
,改善了可讀性而且適用到組件的架構中。使用styled-components
庫,你還能夠獲得SASS
的支持。
條件運算符在React
中很經常使用。它是我用來建立狀態語句的go-to
操做符,在render()
方法中獲得了很好的應用。例如,在下列中它們幫助你之內聯的方式渲染元素,我使用它來顯示登入狀態。
render() { const isLoggedIn = this.state.isLoggedIn; return ( <div> The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in. </div> ); }
然而,當你一次又一次的嵌套條件運算符,它們可能變的醜陋而且難以閱讀。
int median(int a, int b, int c) { return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a : (b<c) ? c : b; }
如你所見,速記符號更加的簡潔,可是它們每每會使代碼看起混亂。想象一下若是在你的結構中有12個或者更多嵌套的條件運算符。這比你所認爲的要常發生。一旦開始使用條件運算符,就很容易繼續嵌套它,最後,您會發現須要一種更好的技術來處理狀態渲染。
但好的方面是你有不少其它的選擇。你可使用加強JSX
控制語句的babel
插件,用來擴展JSX
以包含用於條件語句和循環的組件。
// before transformation <If condition={ test }> <span>Truth</span> </If> // after transformation { test ? <span>Truth</span> : null }
這裏有另一個流行的技術叫作iify
(IIFE
--當即執行函數表達式)。它是一個在聲明以後會當即執行的匿名函數。
(function() { // Do something } )()
爲了使匿名函數成爲函數表達式,咱們將函數封裝在一對括號中。這個模式在JavaScript
中很流行,緣由有不少。可是在React
裏,咱們能夠把全部if/else
語句放到函數中,而後返回咱們想要渲染的內容。
這裏有一個例子演示咱們怎樣在React
中使用IFFE
。
{ (() => { if (this.props.status === 'PENDING') { return (<div className="loading" />); } else { return (<div className="container" />); })() }
IIFE
可能對性能有所影響,可是在大多數狀況下不會有太大的影響。這裏有更多的方法來運行React
中的條件語句,而且咱們已經在用於React中狀態渲染的8個方法中有所說明。
閉包是一個獲取外部函數變量和參數的內部函數。閉包在JavaScript
無處不不在,而且你可能一直使用到它,即便你尚未注意到。
class SayHi extends Component { render () { return () { <Button onClick={(e) => console.log('Say Hi', e)}> Click Me </Button> } } }
可是你在render()
方法中使用閉包時,它確實是很差的。每當SayHi
組件從新渲染,新的匿名會被從新建立並傳入到Button
組件中。雖然porps
沒有改變,可是<Button />
將被強制從新渲染。正如前面所言,重複的渲染會直接帶來性能的損耗。
所以,使用類方法來代替閉包。類方法可讀性更高而且更容易調試。
class SayHi extends Component { showHiMessage = this.showMessage('Hi') render () { return () { <Button onClick={this.showHiMessage}> Click Me </Button> } } }
當一個平臺逐漸擴大,天天都會出現新的模式。一些模式幫助你改善整個工做流程。而其它的一些方式則會有明顯的反作用。當反作用影響應用的性能的時候或者可讀性時,最好是尋找替代方案。在本文中,我介紹了一些由於它們的缺點,你能夠在React
中避免的作法。
您對React的見解和最佳作法是什麼?在評論中分享它們。(想看評論直接去原文看吧)