在React中你能夠中止使用這五種常見寫法

原文: 5 common practices that you can stop doing in React

從下面這點來講,很難說React是這個星球上最受歡迎的庫之一。不少人對React感興趣,新的開發者之因此傾向於使用這個框架的緣由是由於它的UI優先方法。雖然這些年React和它的整個生態已經比較成熟,可是在某些狀況下你仍然會這樣問本身:「正確的作法究竟是什麼?」javascript

這是一個好問題--並不老是有一個通用的「正確」的作事方式。事實上,正如你所知,最佳實踐並不老是那麼的適用。長遠來看,其中的某些寫法可能會影響性能,可讀性,並致使效率低下。html

在本文中,我將描述在React開發中5種被廣泛接受的寫法,可是實際上你是能夠避免這樣作的。固然,我將解釋爲何我認爲這些作法是能夠避免的,而且我會建議你用其餘的寫法來完成相同的事情。前端

從一開始就優化React

React的開發人員在如何使React更快投入了大量的精力,每次迭代新的優化方法又會被添加進去。在我看來,你不該該花費時間優化這些東西,除非你發現了真正的性能影響。java

爲何?react

React相比其它的前端平臺更容易擴展,由於你沒必要重寫某個模塊來加快應用的速度。致使性能問題的罪魁禍首一般是React使用更新虛擬DOMreconciliation過程。webpack

讓咱們看一下React是如何處理下面的事情的。在每一個render()中,React生成了一個由UI元素組成的樹——子節點是實際的DOM元素。當state或者props更新的時候,React須要使用最小的改變次數來生成一個新的樹,並保持可預測性。git

enter image description here

想象一下,你看到的樹是這個樣子的:github

enter image description here

想象一下,你的應用接收到新的數據,下列的節點須要被更新:web

enter image description here

React一般會從新渲染整個子樹,而不是像這樣僅僅渲染相關節點:ajax

enter image description here

當頂層組件的state發生改變的時候,它的子元素都會從新渲染。這是默認的行爲,在小型應用中不須要擔憂。隨着應用的逐漸擴展,您應該考慮使用Chrome Profiling工具來測量實際性能。這個工具將爲你提供在沒必要要的渲染上所浪費時間的精確數據。若是這些數據很重要,你能夠經過向你的組件中添加shouldComponentUpdate鉤子來優化你的渲染時間。

這個鉤子在從新渲染開始以前將被觸發,默認返回true:

shouldComponentUpdate(nextProps, nextState) {
  return true;
}

當返回true時,Reactdiff算法接管並從新渲染整個子樹。你能避免這樣的狀況發生,經過在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

服務端渲染的SEO

雖然單頁面應用很是棒,可是他們仍然存在兩個問題。

  • 當應用首次加載的時候,瀏覽器中沒有JavaScript緩存。若是應用很大,首次加載應用的時間將會很慢。
  • 因爲應用程序是在客戶端進行渲染,搜索引擎使用的web爬蟲程序將沒法索引JavaScript生成的內容。搜索引擎將看到你的應用是空白的,而後排名不好。

這就是服務端渲染技術派上用處的地方。在SSR中,JavaScript內容最初是由服務器渲染的。在首次渲染後,客戶端的腳本接管,並像一個正常的SPA應用運行。因爲你須要使用Node/Express服務,所以在以傳統SSR進行構建時複雜度和花費更高。

若是你是爲了搜索引擎優化、谷歌索引和沒有任何問題地抓取JavaScript內容,那麼有一個好消息。谷歌其實是在2016年開始抓取JavaScript素材的,這個算法如今已經能夠完美地工做了。

如下是2015年10月Webmaster博客的節選。

今天,只要你不阻止Googlebot抓取你的JavaScript或CSS文件,咱們就能 像現代瀏覽器同樣渲染和理解你的網頁。爲了反映這一改進,咱們最近更新了咱們的 技術站長指南,建議不要禁止Googlebot抓取你網站的CSS或JS文件。

若是你使用服務器端渲染,是由於你擔憂你的谷歌頁面排名,那麼你不須要使用SSR。它曾經是過去的事,但如今不是了。

然而,若是你正在改善首次渲染速度,那麼你應該嘗試使用像Next.js這樣的庫來實現更加簡單的SSRNext將節省你在設置Node/Express 服務所花費的時間。

Inline styles和CSS imports

在使用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個方法中有所說明。

在React中的閉包

閉包是一個獲取外部函數變量和參數的內部函數。閉包在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的見解和最佳作法是什麼?在評論中分享它們。(想看評論直接去原文看吧)

相關文章
相關標籤/搜索