React純コンポーネントレンダリング性能逆モード

4851 ワード

React純コンポーネントレンダリング性能逆モード
React純コンポーネントのレンダリングは非常に効率的であり得るが、ユーザはそのデータを可変でないオブジェクトとして使用する必要があり、正常に動作することができる.しかし、JavaScriptのせいで、それをすることは非常に挑戦的かもしれません.
逆モードは、Render関数またはReduxのconnect(mapState)において新たな配列、オブジェクト、関数、または他の新しいオブジェクトを作成することである.
純粋レンダリング
Reactの純粋なレンダリングといえば、コンポーネントは浅い比較によってshouldComponentUpdate方法を実現するべきです.例えば、PurreRendermixin、recompose/pureなどです.
なぜですか
これはあなたがReactでできる最も顕著な性能最適化かもしれません.これもClojureScriptがReactに対してデフォルトで作ったパッケージであり、通常のReactよりも速いと主張する原因である.ClojureScriptは不変のデータ構造を使ってstateを記憶しなければならないので、コンポーネントを再レンダリングするかどうかを判断する時にかかる費用は小さいです.しかしながら、可変データを使用して、データが等しいかどうかを深く比較すると、非常にコストがかかります.ClojureScriptでは、すべてのオブジェクトは常に可変されていませんが、Javascriptではそうではありません.
公正に言うと、純粋なレンダリング最適化が使用されていなくても、Reactは速いが、JavaScriptベースのアニメーション(例えばreact-motion)を使用すると、1 s内のコンポーネントは何千何万回もレンダリングされたり、大きなコンポーネントを使用したりすることができます.低配置のモバイル機器でも、大きな性能が向上します.
逆モード
数ヶ月前に、表計算ドキュメントからユーザーデータをインポートするための編集可能な表を書きました.一枚の表は500人以上のユーザーがいます.一番上のコンポーネントの中で、私が書いたコードは以下の通りです.
class Table extends PureComponent {
  render() {
    return (
      
{this.props.items.map(i => )}
); } }
実際、コードはこれより複雑です.Cellコンポーネントは非常に複雑であり、各ユーザに対して多くのセルをレンダリングしている.したがって、応用には数千個のCell要素がある.
アプリケーションの中で500人のユーザーをロードして、一つのセルを修正してみました.修正した動作は高性能のパソコンで何秒もかかりました.その後、console.log()を使ってコードをデバッグしましたが、小さいセルが変わったら、ほとんどのアプリケーションが再レンダリングされます.これはどうやって可能ですか?私はReduxを使って、アプリケーションの状態を凍結し、可変でないデータを使った.
何時間かの時間を経て頭皮を破り、その中の一つが変わった時に使う配列のデフォルト値を意識しました.
    this.props.options || []
options配列はCell要素に伝達されることが見える.普通、これは何の問題もないです.他のCell要素もレンダリングされないので、属性が一致しているかどうかを確認するために浅い比較ができ、一致しているときレンダリングをスキップすることができますが、もしpropsがnullであれば、デフォルトの配列が使用されます.ご存知のように、配列字面量とnew Array()は、新しい配列例を作成します.これは完全にCell要素内の純粋なコンポーネントレンダリングの最適化を破壊します.Javascriptの異なる例は同じではなく、浅い比較が等しいかどうかは常にfalseに戻り、コンポーネントを再レンダリングするためにReactに通知される.修正の方法はとても簡単です.
const default = [];
class Table extends PureComponent {
  render() {
    return (
      
{this.props.items.map(i => )}
); } }
今操作を変更すると数十ミリ秒しかかかりません.また、defaultPropsの役割は以前と同じです.
関数は新しいオブジェクトを作成します.
renderで関数を作成しても同じ問題があります.多くのコードは次のように書きます.
class App extends PureComponent {
  render() {
    return  this.props.update(e.target.value)} />;
  }
}
または
class App extends PureComponent {
  update(e) {
    this.props.update(e.target.value);
  }
  render() {
    return ;
 }
}
上の配列の字面量と似ています.この2つの場合、新しい関数オブジェクトが作成されます.バインディングを早く実行するべきです.
class App extends PureComponent {
  constructor(props) {
    super(props);
    this.update = this.update.bind(this);
  }
  update(e) {
    this.props.update(e.target.value);
  }
  render() {
    return ;
  }
}
もうちょっと繰り返してください.この問題を解決するために他の方法もあります.thisを使用して、すべての方法を自動的に結合したり、矢印関数にBabelを使用したり、自動バインディング装飾器を使用したりします.
ESLint rule react/jsx-no-bindはこの問題を捕捉するためのツールです.
Reduc React.createClass()にReelectを使用する.
最初は、Redux公式文書で述べられているようなライブラリが重要だとは思いませんでした.Redux connect(mapState)の方法では、性能が低いmap state関数を書くことが少ないからです.私は間違っています.これは関数の性能とは関係がありません.鍵は新しいオブジェクトです.以下のmap state関数を考慮します.
let App = ({otherData, resolution}) => (
  
); const doubleRes = (size) => ({ width: size.width*2, height: size.height*2 }); App = connect(state => { return { otherData: state.otherData, resolution: doubleRes(state.resolution) } })(App);
この例では、stateのconnect()は変化するたびに、otherDataおよびDataContainerは再レンダリングされ、たとえstateのResolutionContainerが変化していなくても.これは、関数resolutionが常に新しいdoubleResオブジェクトを返すからである.Reselectを使ってresolutionを書き換えると、問題は次のようになります.
import {createSelector} from “reselect”;
const doubleRes = createSelector(
  r => r.width,
  r => r.height,
  (width, height) => ({
    width: width*2,
    height: heiht*2
  })
);
Reseelectは前回の関数の結果を覚えています.パラメータが変更されていない場合は、それを返します.
結論
注意すると反パターンがはっきりしますが、依然としてはまりやすいです.比較的に良い方面は、もしあなたが壊れたら、私の前のように、これはあなたの応用を破壊することはできなくて、ただ運行することができるのが少し遅いだけで、大多数の情況の下で、重要ではありません.しかし、私はこの文章の中で、深く研究すべきいくつかの内容を教えてあげたいです.
翻訳はReact.js pure render performance anti-patternからです.