Material-ui: Material-UIのパフォーマンスを向上させる

作成日 2018年03月23日  ·  93コメント  ·  ソース: mui-org/material-ui

まず、この素晴らしいコンポーネントライブラリをありがとうございました! それは素晴らしい!

新しいアプリにドロワーを追加しました。 ほとんどの場合、引き出しの例をコピーして貼り付けました。 PoCのためだけに私は掛けました

        <Divider />
        <List>{mailFolderListItems}</List>

セクション。

その後、特にモバイルデバイス(ネクサス4、クロスウォーク20のコルドバ)では非常に遅く感じます。 私はdevモードを使用していますが、prodモードはあまり高速化されません。

react devツールを介して、すべてのリンクでレンダリングされたmailFolderListItemsのコンポーネントが、react-routerをクリックするか、メニューが開くことに気付きました。 {mailFolderListItems}を再レンダリングするには、最大50〜60ミリ秒かかる場合があります。 私が使う

const modalProps = {
    keepMounted: true, // Better open performance on mobile.
};

他のアプリコンポーネントとの不確実性を排除するために、 mailFolderListItemsをComponentに変換し、再レンダリングを無効にしました。

class MailFolderListItems extends React.Component<{}, {}> {

    shouldComponentUpdate() {
        return false;
    }

    render() {
        return (
            <List>
                <Link to={Routes.Startpage.path}>
                    <ListItem>
                        <ListItemIcon>
                            <InboxIcon />
                        </ListItemIcon>
[...]


                <Divider />
                <MailFolderListItems />

その後、この部分は大丈夫だと感じます。

https://github.com/mui-org/material-ui/issues/5628を見つけました。 再訪することをお勧めします。 shouldComponentUpdate最適化は、パフォーマンスを向上させるための基本的かつ最も重要なステップです。 PureComponentは最も一般的なショートカットです。

さらに、 WithStylesに非常に多くの時間(すべてのマテリアルUIコンポーネントで1〜2ミリ秒以上)が費やされていることに気付きました。

  • [x]このリポジトリの問題を検索し

予想される行動

この素晴らしいライブラリで可能な限りの反応パフォーマンスが得られることを期待しています。

現在の動作

アプリは、すべてのマテリアルUIコンポーネントで遅くなります。

再現手順(バグの場合)

コンポーネントのデモページからコピーして貼り付けただけなので、再現例はまだ提供していませんが、必要に応じてコードサンドボックスのデモを提供できます。 ブラウザの場合、パフォーマンス設定でブラウザの速度が5倍以上遅くなると、目立ちます。

あなたの環境

| 技術| バージョン|
| -------------- | --------- |
| 素材-UI | 1.0.0-beta.38 |
| マテリアル-UIアイコン| 1.0.0-beta.36 |
| React | 16.2.0 |
| ブラウザ| コルドバ横断歩道20(アンドロイドクローム50に等しい)|

discussion performance

最も参考になるコメント

優れた開発者としての@oliviertassinari 、このようなものをどのように書くことができますか? なぜあなたは_あなたの個人的な仮定_を議論として使用し事実を使用し事実と_no_仮定。

あなたのためだけに私は10個のボタンに減らします。 10! これは、material-uiの10個のコンポーネント(さらに悪い場合:コンテナー)が、アプリが使用できなくなるまでアプリ全体の速度を低下させることを意味します。 スロットルなしのクロスウォーク21 /クローム51を備えた実際のデバイスでテスト済み:

通常のボタン:
image

PureButton:
image

これはまだ8倍の改善です! 巨大です! モバイルデバイスでこれがどれほど重要か想像できますか?

100個のボタンの代わりに、1ページに最大10個のボタンがあります。 それでも、10グリッド、10Xなどがあります。

最も単純なコンポーネントの1つであるため、Buttonを使用しました。 これは、パフォーマンスの観点から、material-ui壊れていることを示しています。 ここで、AppBar、ツールバー、リスト、ドロワーなどのコンテナーコンポーネントを想像してみてください。 これはさらに悪いです! すべてのページで20個のコンポーネント/コンテナにすばやくアクセスできます。 また、強力なデスクトップ/ Macで速度が低下しないため、material-uiが信じられないほど遅いわけではありません。

react-intlの場合、前の小道具と新しい小道具の間のチェックは常にfalseになります。 CPUサイクルを浪費します。 つまり、x13-> x0.8

codesandboxの例を見せてください。 なぜこれが起こるべきなのか分かりません。 確かに、これはreactコンポーネントの誤った使用でのみ発生します。 そして公式の例、react-intlが適用されていないコンテキストサブスクライバーのように見えるため、間違った使用法を示しています。 しかし、パフォーマンスを維持するための反応ガイドラインとベストプラクティスに沿った他の多くのコンポーネントがあります。

ところで:WithStylesは、モバイルデバイスのボタンに最大2,27ミリ秒を消費します。 8つのコンポーネントと60fps未満のあなた。

全てのコメント93件

この素晴らしいライブラリで可能な限りの反応パフォーマンスが得られることを期待しています。

@Bessonov Performanceは、v1リリース以降の焦点になります。 ライブラリのコアをできるだけ速く保つように努めました。 ケースの+ 90%には十分なはずです。 少なくとも、これまでのライブラリの使用経験です。 パフォーマンスが重要であるという事実に注意を向ける以外に、外部参照を提供していませんが、この問題を実行可能にすることはできません。 調査できるMaterial-UIのパフォーマンスルート制限を(複製コードサンドボックスまたはリポジトリを使用して)特定できる場合は、それを引き上げてください。 それまでは、問題を解決します。

@oliviertassinari迅速な対応ありがとうございます! リリース後にパフォーマンスが重視されると聞いて、とてもうれしく思います。

ケースの+ 90%には十分なはずです。 少なくとも、これまでのライブラリの使用経験です。

デスクトップ上-はい、そうです。 しかし、モバイルではそれは本当に遅いです。 ドロワーといくつかのボタンだけでアプリが遅くなります。 応答性がなく、必要に応じてより多くの電力を消費します。

パフォーマンスが重要であるという事実に注意を向ける以外に、外部参照を提供していませんが、この問題を実行可能にすることはできません。

すでに提起された問題への参照と、ドキュメントに対応するための参照を提供しました。

調査できるMaterial-UIのパフォーマンスルート制限を特定できる場合(複製コードサンドボックスまたはリポジトリを使用)

私が言ったように、私はそれをすることができます。 これは、純粋なコンポーネントと純粋でないコンポーネントの比較です。 再現する手順は次のとおりです。

  1. Chromeを使用して、 https://codesandbox.io/s/r1ov818nwmにアクセス
  2. プレビューウィンドウで「新しいウィンドウで開く」をクリックします
  3. 開発ツールを開き、「パフォーマンス」タブに移動します
  4. モバイルデバイスを反映するために、CPUを少なくとも5倍(PCによっては-10倍になる場合があります)スロットルします。
  5. ハンバーガーをクリックして、それがどのように遅れるかを確認してください。

そしていま:

  1. index.js
  2. puretrue
  3. 保存する
  4. 上から手順を繰り返します
  5. 楽しめ!

この例は、挿入したList要素が多すぎるため、少し非現実的です。 しかし、私が言ったように、モバイルでは、すべてのアクションを「感じる」場所をすぐに見つけることができます。

これがWithStylesに関する私の問題です。 これは、CPUスロットリングを備えたデスクトップ上にあります。 月曜日に実際のデバイスからスクリーンショットを投稿したいと思います。

WithStyles(ListItem)
image

ListItem
image

違いは、 ListItemコンポーネントだけで1.26ミリ秒です。 このような13のコンポーネントを使用すると、60fpsでレンダリングできなくなります。 しかし、デスクトップで、この期間が常にここにあるとは限らないことに気づきました。

これが純粋な成分と純粋でない成分の比較です

ところで、PureComponentがすべてのパフォーマンス問題の解決策であるとは言いません。 私は、material-uiをモバイルで使用できるようにすることはヘルパーの1つになる可能性があると言っています。

違いは、ListItemコンポーネントだけで1.26ミリ秒です。

@Bessonov WithStylesコンポーネントは、同じコンポーネントの繰り返しインスタンスに対して非常に安価である必要があります。 CSSを注入するのは1回だけです。
たぶん、Reactの制限と高次のコンポーネントのコストに達しています。
Reactのパフォーマンスの問題を軽減するためのさまざまなソリューションがあります。 たとえば、要素のメモ化と仮想化を使用できます。 今後のパフォーマンス調査の出発点として、この問題を開いたままにしておきます。
今のところ、できることはあまりないと思います。 懸念を提起していただきありがとうございます。

わかりました、それはほんの一部です。 より重要な部分、つまりshouldComponentUpdate最適化についてどう思いますか?

編集:私はこの問題を2つの部分に分けたいと思います。 詳細を表示できれば、この部分は約shouldComponentUpdateで、他の部分はWithStylesです。 大丈夫ですか?

shouldComponentUpdateの最適化

@Bessonov 2つの理由から、Material-UI側でこのようなロジックを意図的に実装していません。

  1. ほとんどのコンポーネントはreact要素を受け入れます。これは、レンダリングごとにreact要素の参照が変更されるため、ロジックが体系的にCPUサイクルを浪費します。
  2. shouldComponentUpdateロジックがReactツリーのルートから近いほど、効率が高くなります。 つまり、木の剪定が多ければ多いほどよいということです。 マテリアル-UIコンポーネントは低レベルです。 レバレッジの機会が

私たちが見つけた唯一の機会はアイコンです。 新しいものを特定できるかどうかお知らせください。

WithStylesの他の部分

WithStylesで費やされた時間の+ 90%は実際にはJSSで費やされており、Material-UI側でそれについてできることはほとんどありません。
少なくとも、私は最近、反復レンダリングのパフォーマンスを大幅に向上させるために、サーバー側レンダリングのキャッシュの機会を特定しています。 ただし、これはサーバー側のみです。

ほとんどのコンポーネントはreact要素を受け入れます。これは、レンダリングごとにreact要素の参照が変更されるため、ロジックが体系的にCPUサイクルを浪費します。

それは使用法に依存し、改善することができます。 私は自分自身をベンチマークしていませんが、特に不変の構造(およびimmutable.jsは必要ありません)の場合、適切なコンポーネントの使用と最適化されたshouldComponentUpdate (浅い比較)は、最適化されていないコンポーネントよりもコストが低いと確信していますところで)。 関連チケット: https

たとえば、 https://github.com/mui-org/material-ui/blob/v1-beta/docs/src/pages/demos/drawers/PermanentDrawer.js#L68では、これにより新しいオブジェクトが生成され、 PureComponent無意味:

        classes={{
          paper: classes.drawerPaper,
        }}

新しいオブジェクトが返されるたびに返されるからです。 だがしかし:

const drawerClasses = {
    paper: classes.drawerPaper,
};
[...]
        classes={drawerClasses}

コンポーネントについても同じです。

shouldComponentUpdateロジックがReactツリーのルートから近いほど、効率が高くなります。 つまり、木の剪定が多ければ多いほどよいということです。

はい、本当です。

マテリアル-UIコンポーネントは低レベルです。 レバレッジの機会が少ない。

それは部分的に真実です。 https://codesandbox.io/s/r1ov818nwmでわかるように、 ListPureComponentラップするだけです。 他に何もありません、そしてそれは重要な要因によってドロワーをスピードアップします。 これは、少なくとも{this.props.children}を使用するすべてのコンポーネントに当てはまると思います。 別のデモを作成しました: https

101個のボタン(pure = false)とPureComponentでラップされたボタン(pure = true、Buttonのインポート元を参照)があります。 「クリック」ボタンをクリックすると、同じゲーム結果になります。

通常のボタン:
image

ラップされたボタン(オーバーヘッドなど):
image

ご覧のとおり、通常のボタンだけでは637ミリ秒対47ミリ秒です。 shouldComponentUpdate (または単にPureComponent )を最適化する価値がないとまだ思いますか? :)

編集:最初の文言を修正

@ oliviertassinari @ oreqizer面白いことに気づきました。 拡張ように思えるPureComponentより良いとして行いをComponent

  shouldComponentUpdate() {
    return false;
  }

image

編集:これは反応内部最適化の1つだと思います。

ご覧のとおり、通常のボタンだけでは637ミリ秒対47ミリ秒です。 それでも、shouldComponentUpdate(または単にPureComponent)を最適化する価値はないと思いますか? :)

@Bessonovそれは純粋な論理の可能性を示しています。 はい、とても便利です! これはx13の改善です。 しかし、私はそれが実際の状況に近いとは思いません:

  • 100個のボタンの代わりに、1ページに最大10個のボタンがあります。 それでも、10グリッド、10Xなどがあります。
  • 下位レベルの要素に提供される単純なプロパティの代わりに、react-intlのようなものを使用します。以前の小道具と新しいCPUサイクルを浪費します。 だからx13-> x0.8かそこら

優れた開発者としての@oliviertassinari 、このようなものをどのように書くことができますか? なぜあなたは_あなたの個人的な仮定_を議論として使用し事実を使用し事実と_no_仮定。

あなたのためだけに私は10個のボタンに減らします。 10! これは、material-uiの10個のコンポーネント(さらに悪い場合:コンテナー)が、アプリが使用できなくなるまでアプリ全体の速度を低下させることを意味します。 スロットルなしのクロスウォーク21 /クローム51を備えた実際のデバイスでテスト済み:

通常のボタン:
image

PureButton:
image

これはまだ8倍の改善です! 巨大です! モバイルデバイスでこれがどれほど重要か想像できますか?

100個のボタンの代わりに、1ページに最大10個のボタンがあります。 それでも、10グリッド、10Xなどがあります。

最も単純なコンポーネントの1つであるため、Buttonを使用しました。 これは、パフォーマンスの観点から、material-ui壊れていることを示しています。 ここで、AppBar、ツールバー、リスト、ドロワーなどのコンテナーコンポーネントを想像してみてください。 これはさらに悪いです! すべてのページで20個のコンポーネント/コンテナにすばやくアクセスできます。 また、強力なデスクトップ/ Macで速度が低下しないため、material-uiが信じられないほど遅いわけではありません。

react-intlの場合、前の小道具と新しい小道具の間のチェックは常にfalseになります。 CPUサイクルを浪費します。 つまり、x13-> x0.8

codesandboxの例を見せてください。 なぜこれが起こるべきなのか分かりません。 確かに、これはreactコンポーネントの誤った使用でのみ発生します。 そして公式の例、react-intlが適用されていないコンテキストサブスクライバーのように見えるため、間違った使用法を示しています。 しかし、パフォーマンスを維持するための反応ガイドラインとベストプラクティスに沿った他の多くのコンポーネントがあります。

ところで:WithStylesは、モバイルデバイスのボタンに最大2,27ミリ秒を消費します。 8つのコンポーネントと60fps未満のあなた。

なぜあなたはあなたの個人的な仮定を議論として使用し、事実を使用しないのですか?

それが個人的な仮定だと思う理由は何ですか? 私は批判的思考を実行しようとしていました。 概念的に言えば、余分なプロップトラバーサルは、純粋でないバージョンと比較して純粋なバージョンの速度を低下させます。それだけの価値があるように何かを剪定する必要があります。 #5628または同様の推論https://github.com/react-bootstrap/react-bootstrap/issues/633#issuecomment -234749417またはhttps://github.com/reactstrap/reactstrap/pull/771#issuecomment -375765577

純粋な場合:
capture d ecran 2018-03-27 a 11 43 41

純粋なし:
capture d ecran 2018-03-27 a 11 44 15

再現は以下の通りです。

@oliviertassinari codesandboxがあなたのテストにすべてを正しくすることを確信していますか? 私の結果は非常に異なるため:

純粋なし(スロットルなしでも遅い):
image

純粋(trueに変更して保存した後、codesandboxの新しいURLを取得します):
image

コンテキストの変更をチェックせず、すべての子コンポーネントも「純粋」であることをユーザーに強制するため、これがこのライブラリに適しているとは思いません。 このライブラリは可能な限り柔軟である必要があり、これにより使いにくくなると思います。

要点がわかります。 しかし、これは非常に興味深いトレードオフです。 一方では、material-uiでさえ「可能な限り柔軟」である必要がありますが、他方では、現在のパフォーマンスにより、まったく使用できなくなります。

純粋なバージョンと純粋でないバージョンのコンポーネントを提供することを検討する必要があるかもしれません。 私は今、自分のアプリがデスクトップ上でも使用可能なパフォーマンスを得るためにそれを行っています。

@Bessonovコードサンドボックスが正しく保存されませんでした。 ごめん。 次の差分がありませんでした。 リンクを更新しました:

<Button>
+ <i>
    Button
+ </i>
</Button>

なぜ異なる結果が得られるのかわかりませんか? チャートは良くなりますが、純粋でないバージョンはかなり遅くなります。

編集:わかりました、なるほど。 何が起こっているのか理解してみてください...

はい、分かりました。 まったく同じ「すべてのレンダリングの新しいオブジェクト」-もの。 今まで気づかなかった。 場合によっては、 babelプラグインを介した定数で自動的に改善できます。

さて、あなたはそれをすでに知っています! :Dそれ自体ではあまりメリットはありませんが(約7%を示しました)、上記のいくつかの欠陥を回避するために、純粋なコンポーネントを使用することで結論として利益を得ることができます。 Pure Wrapper + babelプラグイン+プロダクションビルドでテストしましたが、同じモバイルデバイスで高速に動作します。

私が言ったように、柔軟性のための非純粋なコンポーネントとパフォーマンスのための純粋なコンポーネント(ラッパーはそれをシンプルで保守可能に保つのに十分です)の両方を提供するのが最善だと思います。 しかし、私にとっては、全体的なパフォーマンスの向上はパフォーマンスの欠点よりもはるかに大きいため、純粋なコンポーネントだけで生活することができます。 またはそれ以上:純粋なコンポーネントなしでmaterial-uiを使用することはできません。

さて、今のところ、このトピックに関するさらなる入力を待っており、アプリで独自のラッパーを作成します)

洞察をありがとう!

transform-react-constant-elementsが実際に使用され、本当に役立つとは聞いたことがありません。 ランダムなマイクロ最適化と同じくらい問題ありませんが、実際には、そこから多くを得るのに十分なほど単純なコードを書くことはめったにありません。 ただし、 <Add />ようなすべてのSVGスタイルのアイコンコンポーネントにとって悪い最適化ではないと思います。

この例を見てください(横にあるプラグインをクリックし、「react-constant」を検索して、「transform-react-constant-elements」のチェックボックスをクリックしてください)。ほとんど何も最適化されていないことがわかります。

  • 単純な静的InputAdornmentが一番上に移動しました。
  • しかし、簡単な送信ボタンは、イベントハンドラーを配置した瞬間に最適化されなくなりました。
  • そして、InputAdornment自体が持ち上げられている間、 InputProps={{startAdornment: ...}}はまだインラインであり、レンダリングごとに新しいオブジェクトを作成しているため、PureComponentは不可能です。
  • 同様に、 classes={{label: classes.runButtonLabel}}は、PureComponentが[実行]ボタンを最適化することを不可能にします。

私は個人的にPureComponentが好きで、どこでも絶対に使用し、機能するようにできる限り最適化しようとしています。 しかし、MUIがPureComponentが機能するように作成されているようには見えません。

  • *Propsのような小道具InputProps MUIがどのように動作するかの基本パターンです。 これは、必要なときにMUI内部を変更するための高度な方法であるだけでなく、単純なユースケースで定期的に使用されるものです。 このパターンは、通常は純粋モードで最適化できるリーフコンポーネントを最適化できないようにすることがよくあります。
  • 同様に、 classes={{...}}パターンもPureComponentでは機能せず、MUIの項目にスタイルを追加する方法です。 (そして、実際の消費者はコンポーネントの内部クラスとは異なるクラス名を持っている可能性があり、 classesは他の要素のスタイルを設定することを目的としたクラスも含まれている可能性があるため、 classes={classes}を使用すると言うことは決して実用的ではありません同じ消費コンポーネントで)
  • 子供はよく使われますが、1つのコンポーネントを使用する代わりに、一般的なパターンでは2〜3を使用することが多く、可能な場合はそのうちの1つだけを純粋に最適化できます。

何かを最適化したい場合は、これらの基本的な問題に対処する必要があります。そうしないと、純粋モードのMUIを有効にするだけでは、実際にはほとんど最適化されません。 私は2つの可能性を考えることができます。

純粋モードを有効にするだけです。消費者は、実際に最適化を行うために、オブジェクトを手動でメモ化またはホイストする必要があります。

  1. これを行うために私が考えることができる1つの方法は、ローカルのthiskeyを使用するshallowMemoize不足です(したがって、さまざまなデータビットで使用できます)浅い等しい限りデータをメモします
  2. もう1つ考えられるのは、再選択などを使用して、データをメモ化するセレクターを作成することです。
  3. 1.でshallowMemoizeを実行する別の方法は、デコレータを使用してrender()に渡すことです。 このようにして、レンダリングごとに新しいオブジェクトを渡すことができ、 keyを必要とせずに、最後のレンダリングでメモ化されたオブジェクトのいずれかを再利用する必要があるかどうかを確認してから、古い値をすべて破棄できます。

もちろん問題は、これにより消費者がはるかに大きくて混乱し、ロジックを使用するコードから遠く離れた場所に手動でロジックを持ち上げる必要があることです。

import {createSelector} from 'reselect';

class FormPage extends PureComponent {
  state = { example: '' };

  change = e => this.setState({example: e.target.value});
  submit = () => {
    console.log('Submit: ', this.state.example);
  };

  runButtonClasses = createSelector(
    props => props.classes.runButtonLabel,
    runButtonLabel => ({runButtonLabel}));

  render() {
    const {title} = this.props;
    const {example} = this.state;

    return (
      <form>
        {title}
        <TextField
          InputProps={this.shallowMemoize('a', {
            // This example assumes use of transform-react-constant-elements to make this object always the same
            startAdornment: <InputAdornment position="start">Kg</InputAdornment>,
          }}}
          onChange={example}
          value={example} />
        <Button classes={this.runButtonClasses(classes)}>Run</Button>
        <Button onClick={this.submit}>Submit</Button>
      </form>
    );
  }
}
// ...
  <strong i="6">@withShallowMemoize</strong>
  render(memo) {
    const {title} = this.props;
    const {example} = this.state;

    return (
      <form>
        {title}
        <TextField
          InputProps={memo({
            startAdornment: <InputAdornment position="start">Kg</InputAdornment>,
          }}}
          onChange={example}
          value={example} />
        <Button classes={memo(classes)}>Run</Button>
        <Button onClick={this.submit}>Submit</Button>
      </form>
    );
  }

InputProps、クラスなどを最適化しようとする代わりに、すべての使用ケース用のマイクロコンポーネントを作成することをお勧めします

これがMUIの推奨される使用方法である場合、純粋なモードでさえ必要ない場合があります。 ご覧のとおり、一般的なユースケース向けの小さなヘルパーコンポーネントの作成を開始するとすぐに、それらのコンポーネント自体を簡単に純粋なコンポーネントにすることができます。 この例でWeightTextFieldvalueが同じである限り、 valueが変更された場合は、とにかくTextFieldを再レンダリングする必要があるため、純粋でないInputProps={{...}}は問題になりません。

私はこの道で大丈夫です。 私は理論的にはマイクロコンポーネントが好きです。 私が考えることができるそれらを書くための現在有効な構文/パターンをすべて嫌いですが。 MyComponent = enhance(MyComponent)はしたくない、飾りたいのですが、小さなコンポーネントを書く簡単な方法を飾ることはできません。 import {TextField} from 'material-ui';import WeightTextField from '../../../ui/WeightTextField変えるのも嫌いです; `。

`` `js
WeightTextField =({unit、InputProps、... props})=>(
{...小道具}
InputProps = {{
startAdornment:{単位}
... InputProps
}}
onChange = {example}
value = {example} />
);
WeightTextField = pure(WeightTextField);

RunButton = compose(
withStyles(theme =>({
ラベル:{
fontWeight: '800'、
}、
}))、
ピュア
)(ボタン);

const SubmitButton = pure(Button);

クラスFormPageはComponent {を拡張します
状態= {例: ''};

change = e => this.setState({example:e.target.value});
送信=()=> {
console.log( '送信:'、this.state.example);
};

与える() {
const {title} = this.props;
const {example} = this.state;

return (
  <form>
    {title}
    <WeightTextField
      unit='Kg'
      onChange={example}
      value={example} />
    <RunButton>Run</RunButton>
    <SubmitButton onClick={this.submit}>Submit</SubmitButton>
  </form>
);

}
}
「」

大きなリストのページに500〜2000のチェックボックスを表示する必要があるユースケースがあります。 ネイティブブラウザのチェックボックスを使用すると、パフォーマンスは良好ですが、 <Checkbox>コンポーネントを使用すると、パフォーマンスが非常に低くなり、ページ上のチェックボックスの数に比例してスケーリングします。 例: https

私はmui @ nextを使用しています—これを実行可能にするために_now_を採用できる戦略はありますか?

@wilsonjackson
まず、次のことをしないでください。 これにより、レンダリングごとにチェックボックスごとに新しいハンドラーが作成され、実行しようとしているPureComponentの最適化が最適化されなくなります。

  handleChange = index => event => {
    this.setState({

次に、チェックボックスをラップする独自の小さなコンポーネントを作成し、そのコンポーネントを純粋にします。 これには、すべてのチェックボックスに共通のプロパティを追加できるという追加の利点があります。 また、アイテムごとに異なる変更イベントハンドラーが必要になるという一般的な問題があるため、クラスコンポーネントを使用して、リストコンテナーではなくコンポーネントでそれを行うことができます。

https://codesandbox.io/s/r7l64j6v5n

何かを最適化したい場合は、これらの基本的な問題に対処する必要があります。そうしないと、純粋モードのMUIを有効にするだけでは、実際にはほとんど最適化されません。 私は2つの可能性を考えることができます。

@dantmanこれらのAPIの選択は、十分な速度を維持しながらDXを可能な限り改善するために行われました。

InputProps、クラスなどを最適化しようとする代わりに、すべての使用ケース用のマイクロコンポーネントを作成することをお勧めします

はい、あります。 ラッピングパターンは、ライブラリをカスタマイズするための推奨される方法です。 パフォーマンスの最適化を適用するように拡張できます。 コンポーネントの使用法のばらつきがはるかに少ないユーザーランドでは、より簡単です。 この点に関するFAQの質問やガイドセクションをドキュメントに追加することもできます。

はい、あります。 ラッピングパターンは、ライブラリをカスタマイズするための推奨される方法です。 パフォーマンスの最適化を適用するように拡張できます。 コンポーネントの使用法のばらつきがはるかに少ないユーザーランドでは、より簡単です。 この点に関するFAQの質問やガイドセクションをドキュメントに追加することもできます。

Ok。 その場合:

  • 小さなステートレスコンポーネントを簡単に記述できるようにするrecomposeなどのライブラリをお勧めします。 再構成に似ているが、再構成のパターンのような純粋なステートレス関数を構築するが、デコレータ構文を使用するのと同じくらい素晴らしい別のパターンまたはライブラリがあると誰かが私に言ったら、私はそれを歓迎します
  • MUIを使用してjsライブラリでさまざまなオートコンプリートライブラリとcssを使用する方法に関する提案をリストするように、さまざまな既存のフォルダ編成パターンとパスハッキングライブラリ( ../../../../ui/Fooインポートを有効にするもの)も提案したいと思うでしょうsomething-local/Fooようなものに変換します。これを使用して、MUIをラップする独自のローカルシリーズのマイクロコンポーネントを使用して、 import {TextField} from 'material-ui';を使用するのと同じくらい快適にすることができます。 。

@dantman素晴らしい、ありがとう。

withStyles(またはJSS)が非常に遅いため、sCUを適用する必要が何度もあります。 JSSのコードはわかりませんが、かなり最適化できると思います。 私は通常、スタイル付きコンポーネントまたは魅力的なコンポーネントを使用しているため、アプリ内でJSSと他のいずれかが使用され、どちらもJSSよりもパフォーマンスが優れています。

これらのケースは少し面倒かもしれませんが、アプリレベルのsCUまたはよりスマートな状態更新で簡単に回避できます。 単一のMUIコンポーネントが問題になるほど遅いことはまだわかりません。また、実際にMUI内でコーディングして、かなりの時間がかかることもありません。

言うまでもなく、監視が少なくてすればもっと速くなると思いますが、少なくとも私が思うに、その場合はMUIよりもJSSを直接​​最適化することに時間を費やしたほうがよいでしょう。

@Pajnフィードバックをありがとう。 withStylesのパフォーマンスに問題がある状況や、styled-componentsよりもパフォーマンスが優れている状況を見るのは本当に素晴らしいことです。

誰かがこのリポジトリをチェックしましたかhttps://github.com/reactopt/reactopt

$ click - button (text: مقالات ) => CssBaseline,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,ScrollbarSize,TransitionGroup,TouchRipple,Ripple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,Main,ScrollbarSize,TransitionGroup,TouchRipple,Ripple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple

これらのコンポーネントは、クリックするだけで不要な再レンダリングを行います。なぜ、コンポーネントの更新ライフサイクルを試してはいけないのでしょうか。

@ nimaa77

誰かがこのリポジトリをチェックしましたかhttps://github.com/reactopt/reactopt
いいえ、私は明白なwhy-did-you-updateおよびchrome / react開発ツール機能を使用しています。

これらのコンポーネントは、クリックするだけで不要な再レンダリングを行います。なぜ、コンポーネントの更新ライフサイクルを試してはいけないのでしょうか。

上記の説明を参照してください。 これはすべてのユースケースに当てはまるわけではありません。 私にとって、material-uiは、純粋なバージョンと純粋でないバージョンの両方のコンポーネントを提供する必要があるようです。 純粋なコンポーネントを使用すると、パフォーマンスが大幅に向上します。

@dantman

この例を見てください(横にあるプラグインをクリックし、「react-constant」を検索して、「transform-react-constant-elements」のチェックボックスをクリックしてください)。ほとんど何も最適化されていないことがわかります。

これは、この問題に対処する1つの方法にすぎません。 他のオプション、プラグイン、その他の手作りの最適化もあります。 誤解しないでください。しかし、最適化が良いか悪いかについての理論的な議論には興味がありません。 WORKSであるハンズオン最適化に興味があります。 少なくとも私のセットアップでは。 私が上で書いたものはすべて私のために働いており、私のアプリを少なくとも中低域のモバイルデバイスで使用できるようにしています。 コンポーネントツリーの並べ替えなど、さらに最適化を行う必要がありますが、純粋なコンポーネントやその他の自動最適化がなければ、パフォーマンスを達成することはできません。 そして、はい、私はこれを成し遂げるために非常にプロファイリングして最適化します。

@Bessonovおそらく、propを使用してshouldComponentUpdateメソッドを作成して浅い比較を行うことができます(https://reactjs.org/docs/shallow-compare.html)、または常にfalseを返します。
これにより、2つのバージョンのコンポーネント(純粋および通常)でバンドルサイズが増加することはありません。

@lucasljj上記のようなステートフルコンポーネントのラッパーとしてこれを行った場合、バンドルサイズが大幅に増加することはないと思います。 上記のプロファイルも参照してください。純粋なコンポーネントだけがsCUのreturn false;よりも高速です。

純粋なコンポーネントまたはsCUを実装するコンポーネントの問題は、その内部で純粋でないコンポーネントまたはchildrenを使用する場合です。 上記のステートメントを参照してください。 対処すべきもう1つの問題は、テーマの切り替えです。 テストされていませんが、少なくともContextAPIでこれを克服できると思います。

@bossonov Pure Componentsは、パーティーに遅れて来たからではなく、デフォルトのはずの反応と見なされます。 しかし、私はほとんどの人がサブツリーでの不足を軽減するreduxフォームスタイルのラッパーを作成することに同意します

純粋なコンポーネントと子に関して参照する問題は、最上位の小道具が子に伝播しない場合にのみ発生します。 すべての小道具または状態の変更は、小道具が伝播しないレベルまで子ツリーを介して再レンダリングをトリガーします。
純粋なコンポーネントのアイデアは、すべての小道具や状態の変化で再レンダリングするというものだからです。 内部についてはよくわかりませんが、すべての子に渡すコンポーネントごとの変更プロパティを使用することはできないのではないかと思っていました。 ツリー全体に小道具を渡す必要がないように、この目的で新しいコンテキストAPIを使用することもできます。

@oliviertassinari

パフォーマンスは、v1リリース以降の焦点になります。

素晴らしい、v1がリリースされました:)パフォーマンスにいつ対処すべきか考えはありますか? この問題はv1以降のマイルストーンの一部ではないことに気づきました。

@BessonovROADMAPを更新するために少し時間を費やすのは素晴らしいことだと思います。 パフォーマンスについて。 私は実行可能な2つのことを念頭に置いていますが、もっと発見したいと思っています。

  1. 見えないものを改善することはできません。 ベンチマークが必要です。 これまでに2つの興味深いライブラリを見てきました。
  2. CSSサーバー側のレンダリングには、Reactがレンダリングする必要があるものの約30%が必要です。 style = f(props)をサポートしていないため(まあ、まだサポートしていません:#7633)。 非常に効率的なキャッシュ機能を実装して、繰り返し要求される場合のコストを0%近く削減できます。 SSRのパフォーマンスがビジネス指標に悪影響を与える場合は、オフィスでこれに取り組む可能性があります。
    しかし、それだけではありません。Babelプラグインは、実行時ではなくコンパイル時に(バンドルサイズの影響とバランスを取るために)高価なJSSプリセットを適用することも考えられます。cc@ kof。

URLをありがとう。

1に完全に同意し、上記の#4305にリンクします。

  1. SSRは最初のページの読み込みには役立ちますが、遅い再レンダリングやコルドバには役立ちません。 個人的には、デスクトップやモバイルでの初回の読み込みは無視できますが、再レンダリングが遅いと、最初の読み込み後もモバイルデバイスに影響します。

コンパイル時の最適化は素晴らしいものであり、私の観点からは、初回の読み込みと再レンダリングを改善できるため、優先する必要があります。

もう1つは、パフォーマンスを向上させるためにmui(およびbabelなど)を使用する方法に関するドキュメントまたはサンプルプロジェクトの一種です。

jss-cacheが使えるかどうかわかりません。

ISTF +前処理パイプラインは、数ミリ秒を短縮できるようになります。

土、2018年5月19日には、19:06アントンBessonovの[email protected]書きました:

URLをありがとう。

1に完全に同意し、#4305にリンク
上記のhttps://github.com/mui-org/material-ui/issues/4305

  1. SSRは最初のページの読み込みには役立ちますが、遅い場合には役立ちません
    再レンダリングまたはコルドバ。 個人的には、初めての読み込みは無視できます
    デスクトップまたはモバイルでさえ、遅い再レンダリングはその後もモバイルデバイスに影響を与えます
    最初の読み込み。

コンパイル時の最適化は素晴らしいでしょうし、私の観点からは
初回の読み込みと再レンダリングを改善できるため、推奨されます。

もう1つは、muiの使用方法に関するドキュメントまたはサンプルプロジェクトの一種です。
(およびbabelなど)パフォーマンスを向上させます。

jss-cachehttp //cssinjs.org/jss-cache?v = v3.0.0が可能かどうかはわかりません
中古。


あなたが言及されたのであなたはこれを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/mui-org/material-ui/issues/10778#issuecomment-390418709
またはスレッドをミュートします
https://github.com/notifications/unsubscribe-auth/AADOWGrCxNGqrT4MijiX8r9Ad32z6RsJks5t0FEtgaJpZM4S4woq

ベンチマークの設定に役立つ場合があります。
https://github.com/A-gambit/CSS-IN-JS-Benchmarks/blob/master/RESULT.md

ああreact-jss(これはmaterial-uiで使用されています)はかなり遅いようです

@janhoeckそうではなく、他の方法で証明することはできません、私は確信しています。

@kofマイクロベンチマークはあまり意味がないことはよく理解していますが、アフロディーテと比較して4倍から6倍は本当に遅いです。 実際、material-uiの全体的なパフォーマンスは輝いていません。

アフロディーテと比較して4x-6xは本当に遅い

@Bessonovベンチマークの方法論が重要です。 たとえば、次の方法でブラウザで試すことができます。
https://twitter.com/necolas/status/954024318308007937?lang=fr

私のブラウザでの結果:
⚠️不確かさ

capture d ecran 2018-06-12 a 17 58 54

マテリアルの全体的なパフォーマンス-UIは光沢がありません

React自体とコンポーネントのコストに縛られる可能性があります。

@Bessonovは、パフォーマンスのチェックに使用したものは何でも、アフロディーテに関しては真実ではありません。これは、できるだけ早く使用してレンダリングを遅らせるため、ほとんどのベンチマークはCPU時間を測定しますが、最終的なレンダリングパフォーマンスは測定しません。 とはいえ

MUIのパフォーマンスに関しては、抽象化をまったく使用しないと高速化できますが、それは重要ではありません。 次のMUIはかなり高速です。 非常に高速なので、完全に間違ったことをしたり、ライブラリを誤用したりしない限り、パフォーマンスについて心配する必要はありません。

@kofいいえ、MUIはデフォルトでは高速ではありません。 許容できるパフォーマンスを得るために、回避策を実装する必要があります。 確かに、それはモバイルデバイスに関するものであり、ハイエンドのMac向けではないことをご存知でしたか?

あなたが完全に間違ったことをしたり、ライブラリを悪用したりしない限り、そのパフォーマンスについて心配することはありません

さて、上記はいくつかのcodesanboxの例です。 コンポーネントのデモで示したように、コンテナでMUIコンポーネントを使用し、入力値をコンテナの状態で保存すると、非常に速く使用できなくなります。 PureComponentでラップし、制御されていないコンポーネント、ツリーの並べ替え、定数要素、メモ化などを使用して、許容可能なパフォーマンスを維持できるようにします。 より良い結果を得るために、MUIを正しく使用する方法を示すことを歓迎します。たとえば、(atmではドロワーを使用しなくなりました):
https://codesandbox.io/s/r1ov818nwm

@oliviertassinariリンクをありがとう。 ネクサス4でのChrome66の結果は次のとおりです。ご覧のとおり、結果は10倍劣っています。 横断歩道21 /クローム51では少し遅くなる可能性があると思います。

screenshot_2018-06-12-18-20-19

React自体とコンポーネントのコストに縛られる可能性があります。

他の人も言っているように、私見は必ずしもそうではありません。 回避されたすべての再レンダリングは、パフォーマンスとバッテリーにとって大きなメリットです。 パフォーマンスはロードマップの3番目のポイントであることを忘れないでください:+1:

そのように見てください。モバイルデバイスでcssinjsを使用するのが遅すぎる場合は、この反応や他のほとんどのものにも使用しないでください。 cssinjsがあなたを遅くするなら、reactコンポーネントはあなたをもっと遅くします。 レベルobの抽象化を再考するか、最適化を適用する必要があります。

@oliviertassinariはそのevtlです。 muiラッパーが更新時にjssスタイルを再レンダリングする可能性はありますか? 不要な作業を行う可能性のあるリークがある可能性があります。

@kof

そのようにそれを見てください[...]

あなたの言ってる事がわかります。 しかし、反応および反応(純粋)コンポーネントは、それ自体がボトルネックではなく、非常に優れたパフォーマンスを発揮します。 たとえば、MUIはPureComponentを使用しません(上記の長所と短所があります)が、私たちの生活はより安全です。 @oliviertassinariは、キャッシュまたはプリコンパイラを使用することで、パフォーマンスが向上する可能性があると述べています。

誤解しないでください。あなたは素晴らしい人です。すべてのMUIリリースに本当に満足しています:clap:しかし、すべてのユーザーが高性能デスクトップを介してWebサイトにアクセスするわけではないため、パフォーマンスも考慮する必要があり

reactがパフォーマンスのボトルネックではなく、それについて確信がある場合、JSSもそうではありません。 不必要な作業が行われていて、更新時にcssが再生成された場合に、検証する必要があると私が想像できる唯一のことです。

純粋なコンポーネントは、最適化が必要な場合に、アプリケーションレベルのコードで使用する必要があるものです。 MUIはそれを行う必要はありません。これは汎用ライブラリであり、仮定を行うべきではありません。 PureComponentは常に良いとは限りません。

JSSは初日からパフォーマンスを考慮しており、私はマイクロ最適化に無限の時間を費やしました。

ばかげてる。 インタラクションごとにマテリアルUIコンポーネントが再レンダリングされるのを止めることはできません。 私が何をするかは問題ではありません、これと同じくらい簡単なことを前夜に:

class RowText extends Component {
  shouldComponentUpdate = (nextProps, nextState) => {
    return false;
  };

  render() {
    const { title, artist } = this.props;
    return <ListItemText primary={title} secondary={artist} />;
  }
}

基になるタイポグラフィ要素の再レンダリングをトリガーします。 これはどうして可能ですか?

ねえ、これはリストとリストアイテムでのみ発生します。 どうして ? 私にできることはありますか?

@ danielo515完全な例なしではListコンポーネントもコンテキストを使用しているため、その値も変更すると、再レンダリングもトリガーされます。

@ eps1lonを理解しています。 しかし、文脈の変化がどのように起こり得るかを説明していただけますか? 私はリストコンポーネントに何も渡さず、その中の失われた子をレンダリングするだけです。

@ danielo515基本的に、 Listレンダリングされるたび。 新しいコンテキストAPIにより、コンテキスト値をメモ化することで検討できるいくつかの最適化戦略が可能になりました。

現在、値がWRTを厳密に等しくするように変更すると、コンテキストAPIがすべてのコンシューマーで再レンダリングをトリガーします。 Listへのすべてのレンダリング呼び出しで新しいオブジェクトを作成するため、すべてのコンシューマーも再レンダリングされます。

したがって、今のところ、簡単なパフォーマンスブーストは、 Listをラップして、それほど頻繁に再レンダリングされないようにすることです。 ただし、最初にベンチマークを実行するか、早めにレンダリングを中止することをお勧めします。 繰り返されるレンダリング呼び出しTypographyはそれほど悪くないはずです。

コンポーネントをReact.memoラップすると、子がない場合にうまく機能します。 3つのExpansionPanelと約14のFormControlを備えたフォームがあり、デスクトップで遅れています。

これらのパフォーマンスの問題の解決策がなければ、私はmaterial-ui:sを使い続けることができません。

@prevostc例なしで何が悪いのかを判断するのは難しいです。

@prevostcは、コードとボックスの複製を見ずに、これがこの問題に関連しているかどうかを私たちもあなたも知ることができません

@prevostc子を必要としない独自のコンポーネントを作成し、

つまり、データ/イベントの小道具を受け取り、子は受け取らず、単一の拡張パネルのレンダリングを担当する、独自の純粋な/メモされたMyExpansionPanelコンポーネントを作成します。 次に、その<MyExpansionPanel ... />を使用して、各拡張パネルをレンダリングします。 その場合、再レンダリングは1つの拡張パネル(または2つの間で移行する場合は2つ)に制限されます。

@oliviertassinari @kof @dantman
これが私のパフォーマンスの問題のコードサンドボックスの複製です: https ://codesandbox.io/s/yvv2y2zxxx

これは、最大20フィールド(珍しいことではありません)のフォームであり、ユーザー入力に多少の遅れが生じます。 遅いデバイスでは、このフォームは使用できません。

パフォーマンスの問題は、ユーザー入力の大規模な再レンダリングに起因しますが、MUIコンポーネントを純粋なコンポーネント(React.memo)でラップしても、ここにはすべて子と子があるため、AFAIKの再レンダリングが強制されます(ソース:https:// reactjs.org/docs/react-api.html#reactpurecomponent)

以下は、私の手動ベンチマークのスクリーンショットです。1つは最適化なし、1つはすべてがメモ化され、もう1つはフォーム全体で状態を頻繁に設定しないようにローカル状態のカスタム入力コンポーネントを使用しています。
各構成で、ユーザー入力の調整には約60ミリ秒かかります(60fpsでレンダリングする必要がある16ミリ秒をはるかに上回っています。

私はMUIが大好きなので、何か間違ったことをしたことを喜んで学びます。簡単な修正がなかったら悲しいです<3

@dantman入力(子供または小道具)としてReact.ReactNode受け取らないExpansionPanelをどのように書くことができますか? アプリのすべてのパネルに特定のコンポーネントを作成する必要があるということであれば、残念ながらこれは不可能です。コンポーネントが多すぎます。

screenshot 2018-12-20 at 22 04 08

screenshot 2018-12-20 at 21 56 57

screenshot 2018-12-20 at 22 05 00

@dantman入力(子供または小道具)としてReact.ReactNode受け取らないExpansionPanelをどのように書くことができますか? アプリのすべてのパネルに特定のコンポーネントを作成する必要があるということであれば、残念ながらこれは不可能です。コンポーネントが多すぎます。

はい、深くネストされたパネルのツリーで単一の大規模なコンポーネントを作成する代わりに、拡張パネルのピースをコンポーネントに分離します。 そのような巨大な木を最適化することは不可能です。

それは不可能ではありません。 Reactコンポーネントは非常に軽量です。 大規模なコンポーネントから関数にコードのブロックをコピーして貼り付けると、ほぼ完了です。その後は、小道具を接続するだけです。 そして、その機能をReact.memoし、小道具の純粋な最適化を破るようなものを渡さない限り、物事は非常に簡単に最適化されます。

小さなコンポーネントを作成して、大きなコンポーネントからチャンクを分割する場合は、覚えておいてください。 独自のファイルと独自の小道具検証を備えた複雑なものである必要はありません。 これは、propTypesなしで使用されるコンポーネントと同じファイル内の単純な機能コンポーネントにすることができます。 つまり、同じファイル内のコンポーネントでのみ使用できる小さなコンポーネントを作成できます。

はい、マテリアルUIは低レベルのdom要素よりも少し遅いです。 ただし、MUI Inputが生のinputよりも10倍遅く、 Inputを超えると速度が遅くなりすぎた場合でも、MUIがなくても生のinput 10倍速いため、問題が発生します。

@prevostcこれは、拡張パネルを小さな同じファイルコンポーネントに分割することによって最適化されたデモです。 ご覧のとおり、入力が更新されると、1つの拡張パネルのみが再レンダリングされます。 無関係な拡張パネルを再レンダリングするのに時間が無駄になることはありません。 必要に応じて、同様のパターンを共有する入力と同様のことを行うこともできます。その結果、無関係な入力も再レンダリングされません。

これは単なる優れた最適化パターンではなく、優れたコーディングパターンであることに注意してください。 実際のコードでは、入力の配列を作成するのではなく、定義された名前、ラベル、および目的でそれらを明示的に書き出します。 境界を見つけてパターンを繰り返すことは、物事を最適化するだけでなく、定型文を減らしてコードをよりドライで読みやすくします。 つまり、 InputWrapper代わりに、FormControl + InputLabel + FormHelperText + Inputコンボを小さなローカルSimpleTextInputコンポーネントに分割するでしょう。 これは、それを最適化するだけでなく(関連のない入力が再レンダリングされない結果になる)、コードが余分なボイラープレートを繰り返す必要がないことも意味します。

https://codesandbox.io/s/0o7vw76wzp

screen shot 2018-12-20 at 2 51 31 pm

これを読んで、muiを最適化するには、より小さな特定のコンポーネントを作成する必要があるという結論に達しました。 それは私がすでに気づき、成功を収めて試みたものです。 ただし、コンテキストAPIがすべての入力プロパティを変更するため、リストコンポーネントを最適化する方法がないことも理解しました。

よろしく

さて、ここに更新されたストレステストがありますhttps://codesandbox.io/s/wz7yy1kvqk

この一般的な点については@dantmanに同意しますhttps://github.com/mui-org/material-ui/issues/10778#issuecomment-449153635しかし、この少量のコンポーネントではこのようなパフォーマンスの問題は予想していませんでしたが、パフォーマンスの問題の原因を見つけたときの私。

JSSは遅いですか? (ネタバレ注意:いいえ)

このスレッドの以前のコメントのいくつかを参照して、ストレステストにチェックボックスを追加してwithStylesへのすべての呼び出しを削除し、JSSは高速であり、パフォーマンスの問題の原因ではないという結論に達しました。 ( @kofがhttps://github.com/mui-org/material-ui/issues/10778#issuecomment-396609276で指摘したように)。

screenshot 2018-12-22 at 15 17 26

修正

私の特定のユースケースでは、実際に変更された入力は1つだけでしたが、フォームの更新時に各フォーム入力が再レンダリングされるという問題を特定できました。
以下のスクリーンショットでは、FormControlとInputの両方をメモ化されたコンポーネントでラップし、値が変更されなかった場合のレンダリングを回避しました。 @dantmanは実際に、 ExpansionPanelごとに特定のコンポーネントを作成することを提案しましたが、これはあまり一般的ではないソリューションです。 ちなみに、各パネルはまだ再レンダリングされており、パフォーマンスは最適にはほど遠いですが、今のところは十分です。

screenshot 2018-12-22 at 15 18 22

そう? 次は何ですか?

React.ReactNode構成に大きく依存している現在のAPIを大幅に変更しない限り、material-uiコードを変更することでこの種の問題を回避する方法はないと思います。
しかし、 @ dantmanhttps://github.com/mui-org/material-ui/issues/10778#issuecomment -449153635で言及しているように、MUIは予想よりも少し遅いです。 これにまったく対処しないのは私見の間違いです。

この問題を認識しているため、パフォーマンスの問題とその対処方法に関連するドキュメントページを作成する必要がある場合があります。 彼のページが主に公式ドキュメント(https://reactjs.org/docs/optimizing-performance.html)にリダイレクトされ、パフォーマンスの問題を引き起こす可能性のあるコンポーネントがリストされている場合でも、それは始まりです。
このような問題が発生した場合は、console.warnを使用することをお勧めしますが、material-uiレベルで問題を検出する方法がわかりません。

@prevostcこのメッセージは私の一日を作りました、これは私が大好きな種類のコミュニティです。 パフォーマンスを向上させ、ユーザーの土地の最適化の必要性を回避するために、muiがどのように変更できるかについてのアイデアはありますか? APIの変更は実行可能かもしれません。

私はしません:s

現在、MUIの内部を十分に理解していないため、(APIを変更せずに)生のパフォーマンスを改善する方法がわかりません。 私はいくつかのアイデアに取り組んでいますが、今日の時点で決定的なものはありません。ラジオグループが直接の親ではないのに再レンダリングするアプリがありますが、これをローカルで再現することはまだできません。

APIの変更は、APIからReact.ReactNode小道具(子供やアイコンなどの他の小道具)を削除することを検討することですが、同じ構成可能性を維持するための良い方法を見つけることができませんでした。
これが私が試したものです: https

また、開発モードでは反応が特に遅いため、開発モードではMUIが特に遅いことに注意してください。 これを改善する方法があるかどうかはわかりません。

Material-UIが現在直面しているパフォーマンスの問題を最適化するために、機能の追加( @Bessonovが提案したものなど)に進展はありますか?
このすばらしいライブラリをプロジェクトに使用し始めたとき、プロジェクトがどんどん大きくなると、このようなパフォーマンスの問題が発生する可能性があることを知りませんでした。 さらに、Material-UIのドキュメントに、Material-UIの速度が低下してUXに悪影響を与える可能性のあるケースについて通知するセクションがありませんでした。
この号では、Material-UIに直接または間接的に関連する多くのパフォーマンスの問題が報告されています。 誰もが進捗状況を追跡できるように、別の号にそれらをリストするのは良い考えだと思います。 大丈夫だと思ったら、新しい号を開くことができます。

@ mkermani144Material -UIが間違っていることに直接関連するパフォーマンスレポートはまだ見ていません。 これまでのところ、この問題は苦労している人々のためのヘルプフォーラムとして使用されてきました。 実行可能なものが何も報告されていないことを確認しますか? 明らかなことを述べますが、 Reactの抽象化にはコストがかかります。 ホストをラップする各コンポーネントは、レンダリングツリーに重みを追加し、速度を低下させます。 ネイティブホストで100を超えるリストアイテムをレンダリングできますが、それらをクラスコンポーネントでラップすると、問題が発生し始めます。 Material-UIに固有のものではありません。

テーブル

開発モード

テーブルの例を見てみましょう。 それは人々が遅いと感じるコンポーネントです。 仮想化については

次のテストケースでは、開発モードで100個のアイテムをレンダリングします。 次のような場合が考えられます。

  1. テーブル生: https//codesandbox.io/s/v066y5q7z3 :レンダリングで
  2. テーブルマテリアル-UIマスター: https//codesandbox.io/s/m4kwmvj9ly :レンダリングで
  3. テーブルマテリアル-UI次へ: https//codesandbox.io/s/2o35yny1jn :レンダリングで

ホスト要素上DEVモードで素材-UIを使用してのオーバーヘッドので、我々は中間のコンポーネントを作成するという理由だけで、(差は生産が小さい!)x4の周りです。 100個までのテーブルアイテムのリストをレンダリングした後、仮想化が重要になり始めるのはそのためです。 さて、なぜ、そしてそれについて何ができるのかについて少し

  1. Table Raw +関数コンポーネント。 なぜ機能コンポーネントなのか? 使用するクラス名を抽象化したい。 コンポーネントユーザーにそれらを繰り返させたくありません。
    https://codesandbox.io/s/1zl75mwlpj :レンダリングで105ms
  2. テーブルRaw +関数コンポーネント+ forwardRef。 なぜforwardRef? コンポーネントが「透過的」であり、参照を使用してホスト要素にアクセスできるようにする必要があります。
    https://codesandbox.io/s/32o2y0o9op :レンダリングで120ms
  3. テーブルRaw +関数コンポーネント+ forwardRef + withStyles。 なぜwithStyles? コンポーネントのスタイルを設定したいので:
    https://codesandbox.io/s/j2n6pv768y :レンダリングで200ミリ秒
  4. テーブルRaw +関数コンポーネント+ forwardRef + makeStyles。 makeStylesはwithStylesよりも高速である可能性があります。試してみましょう。
    https://codesandbox.io/s/yw52n07l3z :レンダリングで130ms

したがって、利用可能なレバレッジが1つあります。それは、すべてのコンポーネントをwithStylesからmakeStylesに移行することです。 開発モードでは、パフォーマンスの約+ 30%(262 /(262-70))を獲得できます。

生産モード

同じテストケースを本番モードで実行しました。

  • 水和物中n° 130ms
  • 水和物中n° 3106ms
  • 水和物中n° 440ms
  • 水和物中n° 541ms
  • 水和物中n° 680ms
  • 水和物中n° 757ms

したがって、 withStylesからmakeStyles移行は、本番モードでは理論上+ 30%高速化されます。

大丈夫だと思ったら、新しい号を開くことができます。

@ mkermani144 Material-UIが間違っているという特定のケースがある場合は、確かに。

この号の下にあるすべてのコメントについて読みました。 私の問題は、前述の他のどの問題にも当てはまりません。

私が持っているListいくつかの含有する成分ListItemを選択してハイライト表示され、それらのうちの1つを有する、秒。 別のListItemをクリックして選択して強調表示すると、リスト全体(その子を含む)が再度レンダリングされます。

この問題は前のコメントとまったく同じように見えるかもしれませんが、そうではありません。 少なくとも私はそう思う。

Reactプロファイラーの結果を見てみましょう。

image
ご覧のとおり、画像のトップレベルにMyListコンポーネントがあります。 このコンポーネントは、MUI Listラッパーにすぎず、純粋になります。つまり、次のようになります。

class MyList extends React.PureComponent {
  render() {
    return (
      <List>
        {this.props.children}
      </List>
    );
  }
}

彼のコメントの1つで@ eps1lonListを再レンダリングするとコンテキストが更新され、このコンテキストの更新によりすべてのコンシューマー( ListItemを含む)も再レンダリングされると述べているため、このラッパーを追加しました。

また、すべてのListItem純粋にして、アプリのプロファイルを再度作成してみました。 結果は同じでしたが、カスタムコンポーネント(つまりMyListItem )は_itself_を再レンダリングしませんでしたが、その下のすべての子は__did__でした。

MUIがスタイリングに使用するコンテキストが_なんとか_再レンダリングするため、問題が発生することはわかっています。 しかし、なぜこの再レンダリングが発生するのか、どうすれば回避できるのかわかりません。

または、私は何か間違ったことをしていますか?

注:私はMUIの新しい(アルファ)スタイリングソリューション、つまり@material-ui/stylesます。 これが重要かどうかはわかりません。

@ mkermani144ネイティブ要素を含むMaterial-UIを削除し、再レンダリングがまだ存在することを確認します。 純粋なロジックはこのようには役立ちません。 React.createElementは、レンダリングごとに新しい参照を作成し、PureComponentを無効にします。

はい、私は要素がオブジェクトであることを知っています、そしてオブジェクトはJavascriptで厳密に等しくないので、 sCU失敗します。

しかし、 React.createElementが再び呼び出されたと言ったときの意味がわかりません。 createElementへのどの呼び出し? List内の呼び出しを意味する場合、再レンダリングされた場合にのみ、子( ListItem s)に対してcreateElementを呼び出します。 再レンダリングされない場合、 createElementは呼び出されず、再レンダリングは発生しません。 問題は、 List自体が再レンダリングされることです。

@ mkermani144最小限の複製例を作成

MyListMyComponentと呼びます)をレンダリングするコンポーネントが再レンダリングされるため、 MyList (したがってList )が再レンダリングされます。 MyComponentが再レンダリングされ、 MyList新しい子が作成されたため、 MyListのチェックが失敗するため、 MyList PureComponentは役に立ちません。

MyComponentは、選択されたアイテムの状態を保存する場所があるため、おそらく再レンダリングされます。

リストのMUI実装は、レンダリングごとにリストコンテキスト値を再作成しないように変更する必要があると思います
ここ: https

したがって、代わりにリストを次のように表示します。

const List = React.forwardRef(function List(props, ref) {
  const {
    children,
    classes,
    className,
    component: Component,
    dense,
    disablePadding,
    subheader,
    ...other
  } = props;
  const context = React.useMemo(() => ({ dense }), [dense]);

  return (
    <Component
      className={clsx(
        classes.root,
        {
          [classes.dense]: dense && !disablePadding,
          [classes.padding]: !disablePadding,
          [classes.subheader]: subheader,
        },
        className,
      )}
      ref={ref}
      {...other}
    >
      <ListContext.Provider value={context}>
        {subheader}
        {children}
      </ListContext.Provider>
    </Component>
  );
});

これにより、ListItemsのsCUバージョンの作成が簡単になります。

MyListをレンダリングするコンポーネント(MyComponentと呼びます)が再レンダリングされるため、MyList(したがってList)が再レンダリングされます。 MyComponentが再レンダリングされ、MyListの新しい子が作成されたため、MyListのチェックが失敗するため、MyListのPureComponentは役に立ちません。

@Pajnいいえ、 Reactプロファイラーの結果を見てください。 MyListは再レンダリングしませんListは再レンダリングしました(青色)。 MyList PureComponentに固執しません。 MyList sCUを実装して再レンダリングしないようにしていますが、 List __は再レンダリングします__。

@oliviertassinari
最小限の複製例を作成しました。

import React, { Component } from 'react';

import StylesProvider from '@material-ui/styles/StylesProvider';
import ThemeProvider from '@material-ui/styles/ThemeProvider';

import { createMuiTheme } from '@material-ui/core';

import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';

const theme = createMuiTheme({});

const MyListItem = React.memo(ListItem, (prev, next) => prev.selected === next.selected);

class App extends Component {
  state = {
    selected: null,
  }
  render() {
    return (
      <StylesProvider>
        <ThemeProvider theme={theme}>
          <List>
            {[0, 1, 2, 3, 4].map(el => (
              <MyListItem
                button
                selected={el === this.state.selected}
                onClick={() => this.setState({ selected: el })}
              >
                {el}
              </MyListItem>
            ))}
          </List>
        </ThemeProvider>
      </StylesProvider>
    );
  }
}

export default App;

プロファイラーの結果を反応させる(4番目のリスト項目をクリックした後):

image

ご覧のとおり、期待どおりに機能します。つまり、追加の再レンダリングはありません( ListItem内のButtonBaseコンポーネントを除く)。 問題は、この複製が_最小限_すぎることです。 スキップするものがたくさんあります。

私のコードの何が問題で、余分な再レンダリングが発生しているのかを教えてくれないことはわかっています。 しかし、私はあなたに質問をします:MUIコンポーネントをラップするWithStylesInnerコンポーネントで再レンダリングを引き起こす可能性があるのは何ですか?

@ mkermani144この修正についてどう思いますか?

--- a/packages/material-ui/src/List/List.js
+++ b/packages/material-ui/src/List/List.js
@@ -40,6 +40,13 @@ const List = React.forwardRef(function List(props, ref) {
     ...other
   } = props;

+  const context = React.useMemo(
+    () => ({
+      dense,
+    }),
+    [dense],
+  );
+
   return (
     <Component
       className={clsx(
@@ -54,7 +61,7 @@ const List = React.forwardRef(function List(props, ref) {
       ref={ref}
       {...other}
     >
-      <ListContext.Provider value={{ dense }}>
+      <ListContext.Provider value={context}>
         {subheader}
         {children}
       </ListContext.Provider>

プルリクエストを送信しますか? :)Tableコンポーネントでも同じ戦略を使用しています。 それは素晴らしい働きをします。 問題を報告していただきありがとうございます!

@oliviertassinariもちろんです。 これはまさに@Pajnが以前に提案したものです。 PRを提出しました。

14934がマージされました。 ただし、 Listコンポーネントが十分に大きい場合は、最適化されていても、パフォーマンスのボトルネックになる可能性があります。 Tableドキュメントにあるような、 Listコンポーネントでのreact-windowまたはreact-virtualizedの使用法を示す例を提供するべきではありませんか?

Table docsにあるような、react-windowまたはreact-virtualized with Listコンポーネントの使用法を示す例を提供するべきではありませんか?

それは素晴らしいことです:+1:

Fwiwチャットアプリを作成しましたが、アプリは連絡先の大規模なリストをレンダリングする必要があります。 私は同じ問題に遭遇しました
@ mkermani144が持っていた。

https://stackoverflow.com/questions/55969987/why-do-the-children-nodes-rerender-when-the-parent-node-is-not-even-being-update/55971559

@ henrylearn2rock仮想化の使用を検討しましたか? リストのデモを追加しました: https ://next.material-ui.com/demos/lists/#virtualized-list。

これも本当に私をつまずかせました。 ほとんどの人(私を含む)は、純粋なコンポーネントの下にあるすべてのものが再レンダリングから安全であると想定していたと思いますが、このライブラリには当てはまらないようです。 最近提案されたように、仮想化を試してみます。 ありがとう!

ほとんどの人(私を含む)は、純粋なコンポーネントの下にあるすべてのものが再レンダリングから安全であると想定していたと思いますが、このライブラリには当てはまらないようです。

これは、React.PureComponentまたはReact.memoの動作方法ではありません。 コンポーネント自体にのみ影響します。 コンテキストが変更された場合でも、子供は再レンダリングする必要がある場合があります。

@pytyl PureComponentを使用し、そのサブツリーでの再レンダリングを防ぐことを期待したコードを共有できますか?

@ eps1lon次のドキュメントでは、shouldComponentUpdateからfalseを返すと、子コンポーネントでの再レンダリングが自動的にスキップされるように見えます。
https://reactjs.org/docs/optimizing-performance.html#shouldcomponentupdate -in-action

shouldComponentUpdateはC2をルートとするサブツリーに対してfalseを返したため、ReactはC2をレンダリングしようとしませんでした。したがって、C4とC5でshouldComponentUpdateを呼び出す必要さえありませんでした。

多分私はこれを間違えていますか? 以下は私のプロファイラーのスナップショットです。 テストのために、メニューコンポーネントのshouldComponentUpdateに対して明示的にfalseを返します。

Screen Shot 2019-05-08 at 7 46 32 PM

これにより、すべての子コンポーネント(Categories、Category、CategoryItems、CategoryItem)が再レンダリングされなくなりました。 多くのMUI関連のものが下部で再レンダリングされているように見え、これが多くの遅延を引き起こしているようです。 withStyles、Typography、ButtonBaseのようなもの。 Reactはまだ少し新しいので、私の無知を許してください。 以下は、メニューコンポーネントのコードです(shouldComponentUpdateに対してfalseを返します)。

import React, { Component } from "react";
import Categories from "./Categories";
import { withStyles, Paper } from "@material-ui/core";

const styles = theme => ({
  root: {
    paddingTop: 0,
    marginLeft: theme.spacing.unit * 2,
    marginRight: theme.spacing.unit * 2,
    marginTop: theme.spacing.unit * 1
  }
});

class Menu extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    if (nextProps.categories.length == this.props.categories.length) {
      return false;
    }
    return true;
  }

  render() {
    const { classes, categories } = this.props;

    return (
      <Paper className={classes.root}>
        <Categories categories={categories} />
      </Paper>
    );
  }
}

export default withStyles(styles)(Menu);

この問題を理解するには、完全なコードサンドボックスが必要です。

@ eps1lon明日のために

@ eps1lonはここにcodepenです:
https://codesandbox.io/s/348kwwymj5

簡単な説明
これはレストラン向けの基本的なメニューアプリです(多くの場合、100以上のメニュー項目があります)。 ユーザーがメニュー項目をクリックすると、「注文に追加」ダイアログが開きます。 プロファイラーのパフォーマンスが低下している状況をいくつか添付します(これらの統計は本番ビルドではありません)。

システム
MacBook Pro(Retina、13インチ、2015年初頭)
3.1 GHz Intel Core i7
Firefox 66.0.3

ケース1(ユーザーがメニュー項目をクリックする)
レンダリング時間:218ms

Screen Shot 2019-05-10 at 4 45 26 AM

Screen Shot 2019-05-10 at 4 45 48 AM

ケース2(ユーザーがダイアログの[注文に追加]ボタンをクリックする)
レンダリング時間:356ms

Screen Shot 2019-05-10 at 4 46 24 AM

Screen Shot 2019-05-10 at 4 47 10 AM

私はここで初心者の間違いを犯していると確信しているので、どんなガイダンスも大歓迎です。

WithStyles(ButtonBase)が再レンダリングされるので、WithStylesは、必要がなくても再作成されるコンテキストを使用すると思います。

このhttps://github.com/mui-org/material-ui/blob/048c9ced0258f38aa38d95d9f1cfa4c7b993a6a5/packages/material-ui-styles/src/StylesProvider/StylesProvider.js#L38を見つけることができましたが、場所が見つかりませんStylesProviderは実際のコードで使用されます(GitHubs検索はあまり良くありません)が、それが理由かもしれません。

@ eps1lonは、これが原因である可能性があるかどうかを知っていますか? もしそうなら、コンテキストオブジェクトのuseMemoはおそらくこれを修正するでしょう。 localOptionsが安定しているかどうか、またはuseMemoをさらに伝播する必要があるかどうかはわかりませんが。

多分。 ただし、最初に、StylesProviderを使用するコンポーネントが再レンダリングされる理由を調査する必要があります。 これは、ツリーの最上部にあるか、UIの境界にある必要があります。 どちらの場合でも、それらはめったに再レンダリングされるべきではありません。 とにかく、reactコンテキストは頻繁な更新用に最適化されていないことに注意してください。

レンダリング中に一部のオブジェクトが再作成されたという理由だけで、これらを時期尚早に最適化するべきではありません。 メモ化は特効薬ではありません。 したがって、具体的な例がなければ、私は多くのことを行うことができません。 はい、再レンダリングします。 時にはもっと頻繁に彼らはそうする必要があります。 しかし、無駄な再レンダリングは、それがパフォーマンスのボトルネックの原因であるという意味ではありません。

@pytylコードサンドボックスを確認しましたが、レンダリングアーキテクチャに問題があります。 メニュー項目をクリックすると、すべてが再レンダリングされます。 GlobalContextは純粋なロジックをジャンプします。

@ eps1lonこの問題を解決する必要があると思います。 具体的に特定された問題に焦点を当てたほうがよいでしょう。

TL; DR:コンテキストスライスの作成、コンテキスト値のメモ化、特にmaterial-uiの問題なし: https ://codesandbox.io/s/8lx6vk2978

いくつか掘り下げましたが、問題は、レンダリング中に再作成されるこの大きなグローバルコンテキストがあることです。 クリックするとアプリが再レンダリングされ、その時点でグローバルコンテキストが再作成されます。 あなたのCategoryItemはあなたのアプリに100回現れるそれを聞いています。 100個のmaterial-uiMenuItemがあるので、1000カットで古典的な死に遭遇します。

したがって、皮肉なことに、ソリューションの一部はコンテキスト値をメモ化することですが、重要な部分は個別のコンテキストスライスを識別することです。 状態とディスパッチコンテキストが適切であるように思われます。 これは、useReducerでuseContextを使用する場合に推奨され、ここに適合するようです。

これにより、非常に大きなツリーが作成され、コンテキストが増えるほど小道具が地獄になります。 useContextをご覧になることをお勧めします。 これらの問題に直面し始めた場合、それは大いに役立ちます。

@oliviertassinari解決策でよくある落とし穴を集めるのは良い問題です。 それとは別の問題を作成するかどうかを決定できます。

@oliviertassinari @ eps1lon改訂してくれてありがとう! パフォーマンスは素晴らしいようです。

レンダリングパフォーマンスが遅いという問題がありました。 <Box>コンポーネントのすべてのインスタンスを<div>置き換えることで、完全に解決しました。 私はreactdevtoolsフレームグラフを使用してデバッグし、約420ミリ秒から20ミリ秒になりました。

<Box> esで;
Screen Shot 2019-08-16 at 12 47 25 AM

<Box> esなし:
Screen Shot 2019-08-16 at 12 42 38 AM

@mankittensスタイルエンジンとしてstyled-componentsを使用して、Boxコンポーネントを保持できます。 パフォーマンスははるかに良くなります。 近い将来、JSSで改善されるはずですhttps://github.com/mui-org/material-ui/pull/16858。

この号を締めくくります。 包括的なスレッドではなく、改善の可能性のある領域ごとに専用のパフォーマンスレポートが必要です。

このページは役に立ちましたか?
0 / 5 - 0 評価