React Render Array 性能大亂斗

現(xiàn)在關(guān)于 React 最新 v16 版本新特性的宣傳、講解已經(jīng)“鋪天蓋地”了愕撰。你最喜歡哪一個 new feature刹衫?
截至目前,組件構(gòu)建方式已經(jīng)琳瑯滿目搞挣。那么带迟,你考慮過他們的性能對比嗎?這篇文章囱桨,聚焦其中一個小細節(jié)仓犬,進行對比,望讀者參考的同時舍肠,期待大神斧正搀继。

從 React.PureComponent 說起

先上結(jié)論:在我們的測試當(dāng)中窘面,使用 React.PureComponent 能夠提升 30% JavaScript 執(zhí)行效率。測試場景是反復(fù)操作數(shù)組律歼,這個“反復(fù)操作”有所講究民镜,我們計劃持續(xù)不斷地改變數(shù)組的某一項(而不是整個數(shù)組的大范圍變動)。

線上參考地址: 請點擊這里

那么這樣的場景险毁,作為開發(fā)者有必要研究嗎制圈?如果你的應(yīng)用并不涉及到高頻率的更新數(shù)組某幾項,那么大可不必在意這些性能的微妙差別畔况。但是如果存在一些“實時更新”的場景鲸鹦,比如:

  • 用戶輸入改變數(shù)組(點贊者顯示);
  • 輪詢(股票實時)跷跪;
  • 推更新(比賽比分實時播報)馋嗜;

那么就需要進行考慮。我們定義:changedItems.length / array.length 比例越小吵瞻,本文所涉及的性能優(yōu)化越應(yīng)該實施葛菇,即越有必要使用 React.PureComponent。

代碼和性能測試

在使用 React 開發(fā)時橡羞,相信很多開發(fā)者在搭配函數(shù)式的狀態(tài)管理框架 Redux 使用眯停。Redux reducers 作為純函數(shù)的同時,也要保證 state 的不可變性卿泽,在我們的場景中莺债,也就是說在相關(guān) action 被觸發(fā)時,需要返回一個新的數(shù)組签夭。

const users = (state, action) => {
  if (action.type === 'CHANGE_USER_1') {
    return [action.payload, ...state.slice(1)]
  }
  return state
}

如上代碼齐邦,當(dāng) CHANGE_USER_1 時,我們對數(shù)組的第一項進行更新第租,使用 slice 方法措拇,不改變原數(shù)組的同時返回新的數(shù)組。

我們設(shè)想所有的 users 數(shù)組被 Users 函數(shù)式組件渲染:

import User from './User'
const Users = ({users}) =>
  <div>
    {
      users.map(user => <User {...user} />
    }
  </div>

問題的關(guān)鍵在于:users 數(shù)組作為 props 出現(xiàn)慎宾,當(dāng)數(shù)組中的第 K 項改變時丐吓,所有的 <User> 組件都會進行 reconciliation 的過程,即使非 K 項并沒有發(fā)生變化璧诵。

這時候汰蜘,我們可以引入 React.PureComponent,它通過淺對比規(guī)避了不必要的更新過程之宿。即使淺對比自身也有計算成本族操,但是一般情況下這都不值一提。

以上內(nèi)容其實已經(jīng)“老生常談”了,下面直接進入代碼和性能測試環(huán)節(jié)色难。

我們渲染了一個有 200 項的數(shù)組:

const arraySize = 200;
const getUsers = () =>
  Array(arraySize)
    .fill(1)
    .map((_, index) => ({
      name: 'John Doe',
      hobby: 'Painting',
      age: index === 0 ? Math.random() * 100 : 50
    }));

注意在 getUsers 方法中泼舱,關(guān)于 age 屬性我們做了判斷,保證每次調(diào)用時枷莉,getUsers 返回的數(shù)組只有第一項的 age 屬性不同娇昙。
這個數(shù)組將會觸發(fā) 400 次 re-renders 過程,并且每一次只改變數(shù)組第一項的一個屬性(age):

  const repeats = 400;
  componentDidUpdate() {
    ++this.renderCount;
    this.dt += performance.now() - this.startTime;
    if (this.renderCount % repeats === 0) {
      if (this.componentUnderTestIndex > -1) {
        this.dts[componentsToTest[this.componentUnderTestIndex]] = this.dt;
        console.log(
          'dt',
          componentsToTest[this.componentUnderTestIndex],
          this.dt
        );
      }
      ++this.componentUnderTestIndex;
      this.dt = 0;
      this.componentUnderTest = componentsToTest[this.componentUnderTestIndex];
    }
    if (this.componentUnderTest) {
      setTimeout(() => {
        this.startTime = performance.now();
        this.setState({ users: getUsers() });
      }, 0);
    } else {
      alert(`
        Render Performance ArraySize: ${arraySize} Repeats: ${repeats}
        Functional: ${Math.round(this.dts.Functional)} ms
        PureComponent: ${Math.round(this.dts.PureComponent)} ms
        Component: ${Math.round(this.dts.Component)} ms
      `);
    }
  }

為此笤妙,我們采用三種方式設(shè)計 <User> 組件冒掌。

函數(shù)式方式

export const Functional = ({ name, age, hobby }) => (
  <div>
    <span>{name}</span>
    <span>{age}</span>
    <span>{hobby}</span>
  </div>
);

PureComponent 方式

export class PureComponent extends React.PureComponent {
  render() {
    const { name, age, hobby } = this.props;
    return (
      <div>
        <span>{name}</span>
        <span>{age}</span>
        <span>{hobby}</span>
      </div>
    );
  }
}

經(jīng)典 class 方式

export class Component extends React.Component {
  render() {
    const { name, age, hobby } = this.props;
    return (
      <div>
        <span>{name}</span>
        <span>{age}</span>
        <span>{hobby}</span>
      </div>
    );
  }
}

同時,在不同的瀏覽器環(huán)境下蹲盘,我得出:

  • Firefox 下股毫,PureComponent 收益 30%;
  • Safari 下召衔,PureComponent 收益 6%铃诬;
  • Chrome 下,PureComponent 收益 15%苍凛;

測試硬件環(huán)境:

機器

最終結(jié)果:

最后趣席,送給大家魯迅先生的一句話:

“Early optimization is the root of all evil”?- 魯迅

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市醇蝴,隨后出現(xiàn)的幾起案子宣肚,更是在濱河造成了極大的恐慌,老刑警劉巖哑蔫,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钉寝,死亡現(xiàn)場離奇詭異弧呐,居然都是意外死亡闸迷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門俘枫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腥沽,“玉大人,你說我怎么就攤上這事鸠蚪〗裱簦” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵茅信,是天一觀的道長盾舌。 經(jīng)常有香客問我,道長蘸鲸,這世上最難降的妖魔是什么妖谴? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上膝舅,老公的妹妹穿的比我還像新娘嗡载。我一直安慰自己,他們只是感情好仍稀,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布洼滚。 她就那樣靜靜地躺著,像睡著了一般技潘。 火紅的嫁衣襯著肌膚如雪遥巴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天享幽,我揣著相機與錄音挪哄,去河邊找鬼。 笑死琉闪,一個胖子當(dāng)著我的面吹牛迹炼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播颠毙,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼斯入,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蛀蜜?” 一聲冷哼從身側(cè)響起刻两,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滴某,沒想到半個月后磅摹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡霎奢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年户誓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片幕侠。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡帝美,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出晤硕,到底是詐尸還是另有隱情悼潭,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布舞箍,位于F島的核電站舰褪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏疏橄。R本人自食惡果不足惜占拍,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧刷喜,春花似錦残制、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至浊闪,卻和暖如春恼布,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背搁宾。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工折汞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盖腿。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓爽待,卻偏偏與公主長得像,于是被迫代替她去往敵國和親翩腐。 傳聞我的和親對象是個殘疾皇子鸟款,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)茂卦,斷路器何什,智...
    卡卡羅2017閱讀 134,659評論 18 139
  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,237評論 0 4
  • 原教程內(nèi)容詳見精益 React 學(xué)習(xí)指南,這只是我在學(xué)習(xí)過程中的一些閱讀筆記等龙,個人覺得該教程講解深入淺出处渣,比目前大...
    leonaxiong閱讀 2,839評論 1 18
  • 深入JSX date:20170412筆記原文其實JSX是React.createElement(componen...
    gaoer1938閱讀 8,069評論 2 35
  • 今天的晨讀迭代了我對成功的認知罐栈。在遙遠的學(xué)生時代,我們一直秉持“失敗是成功之母”這樣的觀點暴备;再后來悠瞬,辯證性的觀點告...
    Jessie_杰茜閱讀 166評論 5 8