まず、この素晴らしいコンポーネントライブラリをありがとうございました! それは素晴らしい!
新しいアプリにドロワーを追加しました。 ほとんどの場合、引き出しの例をコピーして貼り付けました。 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ミリ秒以上)が費やされていることに気付きました。
この素晴らしいライブラリで可能な限りの反応パフォーマンスが得られることを期待しています。
アプリは、すべてのマテリアルUIコンポーネントで遅くなります。
コンポーネントのデモページからコピーして貼り付けただけなので、再現例はまだ提供していませんが、必要に応じてコードサンドボックスのデモを提供できます。 ブラウザの場合、パフォーマンス設定でブラウザの速度が5倍以上遅くなると、目立ちます。
| 技術| バージョン|
| -------------- | --------- |
| 素材-UI | 1.0.0-beta.38 |
| マテリアル-UIアイコン| 1.0.0-beta.36 |
| React | 16.2.0 |
| ブラウザ| コルドバ横断歩道20(アンドロイドクローム50に等しい)|
この素晴らしいライブラリで可能な限りの反応パフォーマンスが得られることを期待しています。
@Bessonov Performanceは、v1リリース以降の焦点になります。 ライブラリのコアをできるだけ速く保つように努めました。 ケースの+ 90%には十分なはずです。 少なくとも、これまでのライブラリの使用経験です。 パフォーマンスが重要であるという事実に注意を向ける以外に、外部参照を提供していませんが、この問題を実行可能にすることはできません。 調査できるMaterial-UIのパフォーマンスルート制限を(複製コードサンドボックスまたはリポジトリを使用して)特定できる場合は、それを引き上げてください。 それまでは、問題を解決します。
@oliviertassinari迅速な対応ありがとうございます! リリース後にパフォーマンスが重視されると聞いて、とてもうれしく思います。
ケースの+ 90%には十分なはずです。 少なくとも、これまでのライブラリの使用経験です。
デスクトップ上-はい、そうです。 しかし、モバイルではそれは本当に遅いです。 ドロワーといくつかのボタンだけでアプリが遅くなります。 応答性がなく、必要に応じてより多くの電力を消費します。
パフォーマンスが重要であるという事実に注意を向ける以外に、外部参照を提供していませんが、この問題を実行可能にすることはできません。
すでに提起された問題への参照と、ドキュメントに対応するための参照を提供しました。
調査できるMaterial-UIのパフォーマンスルート制限を特定できる場合(複製コードサンドボックスまたはリポジトリを使用)
私が言ったように、私はそれをすることができます。 これは、純粋なコンポーネントと純粋でないコンポーネントの比較です。 再現する手順は次のとおりです。
そしていま:
index.js
pure
をtrue
この例は、挿入したList要素が多すぎるため、少し非現実的です。 しかし、私が言ったように、モバイルでは、すべてのアクションを「感じる」場所をすぐに見つけることができます。
これがWithStyles
に関する私の問題です。 これは、CPUスロットリングを備えたデスクトップ上にあります。 月曜日に実際のデバイスからスクリーンショットを投稿したいと思います。
WithStyles(ListItem)
:
ListItem
:
違いは、 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側でこのようなロジックを意図的に実装していません。
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でわかるように、 List
をPureComponent
ラップするだけです。 他に何もありません、そしてそれは重要な要因によってドロワーをスピードアップします。 これは、少なくとも{this.props.children}
を使用するすべてのコンポーネントに当てはまると思います。 別のデモを作成しました: https :
101個のボタン(pure = false)とPureComponentでラップされたボタン(pure = true、Buttonのインポート元を参照)があります。 「クリック」ボタンをクリックすると、同じゲーム結果になります。
通常のボタン:
ラップされたボタン(オーバーヘッドなど):
ご覧のとおり、通常のボタンだけでは637ミリ秒対47ミリ秒です。 shouldComponentUpdate
(または単にPureComponent
)を最適化する価値がないとまだ思いますか? :)
編集:最初の文言を修正
@ oliviertassinari @ oreqizer面白いことに気づきました。 拡張ように思えるPureComponent
より良いとして行いをComponent
と
shouldComponentUpdate() {
return false;
}
編集:これは反応内部最適化の1つだと思います。
ご覧のとおり、通常のボタンだけでは637ミリ秒対47ミリ秒です。 それでも、shouldComponentUpdate(または単にPureComponent)を最適化する価値はないと思いますか? :)
@Bessonovそれは純粋な論理の可能性を示しています。 はい、とても便利です! これはx13の改善です。 しかし、私はそれが実際の状況に近いとは思いません:
優れた開発者としての@oliviertassinari 、このようなものをどのように書くことができますか? なぜあなたは_あなたの個人的な仮定_を議論として使用し、事実を使用し事実と_no_仮定。
あなたのためだけに私は10個のボタンに減らします。 10! これは、material-uiの10個のコンポーネント(さらに悪い場合:コンテナー)が、アプリが使用できなくなるまでアプリ全体の速度を低下させることを意味します。 スロットルなしのクロスウォーク21 /クローム51を備えた実際のデバイスでテスト済み:
通常のボタン:
PureButton:
これはまだ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
純粋な場合:
純粋なし:
再現は以下の通りです。
@oliviertassinari codesandboxがあなたのテストにすべてを正しくすることを確信していますか? 私の結果は非常に異なるため:
純粋なし(スロットルなしでも遅い):
純粋(trueに変更して保存した後、codesandboxの新しいURLを取得します):
コンテキストの変更をチェックせず、すべての子コンポーネントも「純粋」であることをユーザーに強制するため、これがこのライブラリに適しているとは思いません。 このライブラリは可能な限り柔軟である必要があり、これにより使いにくくなると思います。
要点がわかります。 しかし、これは非常に興味深いトレードオフです。 一方では、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
が一番上に移動しました。InputProps={{startAdornment: ...}}
はまだインラインであり、レンダリングごとに新しいオブジェクトを作成しているため、PureComponentは不可能です。classes={{label: classes.runButtonLabel}}
は、PureComponentが[実行]ボタンを最適化することを不可能にします。私は個人的にPureComponentが好きで、どこでも絶対に使用し、機能するようにできる限り最適化しようとしています。 しかし、MUIがPureComponentが機能するように作成されているようには見えません。
*Props
のような小道具InputProps
MUIがどのように動作するかの基本パターンです。 これは、必要なときにMUI内部を変更するための高度な方法であるだけでなく、単純なユースケースで定期的に使用されるものです。 このパターンは、通常は純粋モードで最適化できるリーフコンポーネントを最適化できないようにすることがよくあります。classes={{...}}
パターンもPureComponentでは機能せず、MUIの項目にスタイルを追加する方法です。 (そして、実際の消費者はコンポーネントの内部クラスとは異なるクラス名を持っている可能性があり、 classes
は他の要素のスタイルを設定することを目的としたクラスも含まれている可能性があるため、 classes={classes}
を使用すると言うことは決して実用的ではありません同じ消費コンポーネントで)何かを最適化したい場合は、これらの基本的な問題に対処する必要があります。そうしないと、純粋モードのMUIを有効にするだけでは、実際にはほとんど最適化されません。 私は2つの可能性を考えることができます。
this
とkey
を使用するshallowMemoize
不足です(したがって、さまざまなデータビットで使用できます)浅い等しい限りデータをメモします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>
);
}
これがMUIの推奨される使用方法である場合、純粋なモードでさえ必要ない場合があります。 ご覧のとおり、一般的なユースケース向けの小さなヘルパーコンポーネントの作成を開始するとすぐに、それらのコンポーネント自体を簡単に純粋なコンポーネントにすることができます。 この例でWeightTextField
、 value
が同じである限り、 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>
);
}
}
「」
@wilsonjackson
まず、次のことをしないでください。 これにより、レンダリングごとにチェックボックスごとに新しいハンドラーが作成され、実行しようとしているPureComponentの最適化が最適化されなくなります。
handleChange = index => event => {
this.setState({
次に、チェックボックスをラップする独自の小さなコンポーネントを作成し、そのコンポーネントを純粋にします。 これには、すべてのチェックボックスに共通のプロパティを追加できるという追加の利点があります。 また、アイテムごとに異なる変更イベントハンドラーが必要になるという一般的な問題があるため、クラスコンポーネントを使用して、リストコンテナーではなくコンポーネントでそれを行うことができます。
何かを最適化したい場合は、これらの基本的な問題に対処する必要があります。そうしないと、純粋モードのMUIを有効にするだけでは、実際にはほとんど最適化されません。 私は2つの可能性を考えることができます。
@dantmanこれらのAPIの選択は、十分な速度を維持しながらDXを可能な限り改善するために行われました。
InputProps、クラスなどを最適化しようとする代わりに、すべての使用ケース用のマイクロコンポーネントを作成することをお勧めします
はい、あります。 ラッピングパターンは、ライブラリをカスタマイズするための推奨される方法です。 パフォーマンスの最適化を適用するように拡張できます。 コンポーネントの使用法のばらつきがはるかに少ないユーザーランドでは、より簡単です。 この点に関するFAQの質問やガイドセクションをドキュメントに追加することもできます。
はい、あります。 ラッピングパターンは、ライブラリをカスタマイズするための推奨される方法です。 パフォーマンスの最適化を適用するように拡張できます。 コンポーネントの使用法のばらつきがはるかに少ないユーザーランドでは、より簡単です。 この点に関するFAQの質問やガイドセクションをドキュメントに追加することもできます。
Ok。 その場合:
../../../../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つのことを念頭に置いていますが、もっと発見したいと思っています。
style = f(props)
をサポートしていないため(まあ、まだサポートしていません:#7633)。 非常に効率的なキャッシュ機能を実装して、繰り返し要求される場合のコストを0%近く削減できます。 SSRのパフォーマンスがビジネス指標に悪影響を与える場合は、オフィスでこれに取り組む可能性があります。URLをありがとう。
1に完全に同意し、上記の#4305にリンクします。
コンパイル時の最適化は素晴らしいものであり、私の観点からは、初回の読み込みと再レンダリングを改善できるため、優先する必要があります。
もう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 。
- 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
私のブラウザでの結果:
⚠️不確かさ
マテリアルの全体的なパフォーマンス-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では少し遅くなる可能性があると思います。
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
をどのように書くことができますか? アプリのすべてのパネルに特定のコンポーネントを作成する必要があるということであれば、残念ながらこれは不可能です。コンポーネントが多すぎます。
@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コンポーネントに分割するでしょう。 これは、それを最適化するだけでなく(関連のない入力が再レンダリングされない結果になる)、コードが余分なボイラープレートを繰り返す必要がないことも意味します。
これを読んで、muiを最適化するには、より小さな特定のコンポーネントを作成する必要があるという結論に達しました。 それは私がすでに気づき、成功を収めて試みたものです。 ただし、コンテキストAPIがすべての入力プロパティを変更するため、リストコンポーネントを最適化する方法がないことも理解しました。
よろしく
さて、ここに更新されたストレステストがありますhttps://codesandbox.io/s/wz7yy1kvqk
この一般的な点については@dantmanに同意しますhttps://github.com/mui-org/material-ui/issues/10778#issuecomment-449153635しかし、この少量のコンポーネントではこのようなパフォーマンスの問題は予想していませんでしたが、パフォーマンスの問題の原因を見つけたときの私。
このスレッドの以前のコメントのいくつかを参照して、ストレステストにチェックボックスを追加してwithStyles
へのすべての呼び出しを削除し、JSSは高速であり、パフォーマンスの問題の原因ではないという結論に達しました。 ( @kofがhttps://github.com/mui-org/material-ui/issues/10778#issuecomment-396609276で指摘したように)。
私の特定のユースケースでは、実際に変更された入力は1つだけでしたが、フォームの更新時に各フォーム入力が再レンダリングされるという問題を特定できました。
以下のスクリーンショットでは、FormControlとInputの両方をメモ化されたコンポーネントでラップし、値が変更されなかった場合のレンダリングを回避しました。 @dantmanは実際に、 ExpansionPanel
ごとに特定のコンポーネントを作成することを提案しましたが、これはあまり一般的ではないソリューションです。 ちなみに、各パネルはまだ再レンダリングされており、パフォーマンスは最適にはほど遠いですが、今のところは十分です。
React.ReactNode
構成に大きく依存している現在のAPIを大幅に変更しない限り、material-uiコードを変更することでこの種の問題を回避する方法はないと思います。
しかし、 @ dantmanがhttps://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個のアイテムをレンダリングします。 次のような場合が考えられます。
ホスト要素上DEVモードで素材-UIを使用してのオーバーヘッドので、我々は中間のコンポーネントを作成するという理由だけで、(差は生産が小さい!)x4の周りです。 100個までのテーブルアイテムのリストをレンダリングした後、仮想化が重要になり始めるのはそのためです。 さて、なぜ、そしてそれについて何ができるのかについて少し
したがって、利用可能なレバレッジが1つあります。それは、すべてのコンポーネントをwithStylesからmakeStylesに移行することです。 開発モードでは、パフォーマンスの約+ 30%(262 /(262-70))を獲得できます。
同じテストケースを本番モードで実行しました。
したがって、 withStyles
からmakeStyles
移行は、本番モードでは理論上+ 30%高速化されます。
大丈夫だと思ったら、新しい号を開くことができます。
@ mkermani144 Material-UIが間違っているという特定のケースがある場合は、確かに。
この号の下にあるすべてのコメントについて読みました。 私の問題は、前述の他のどの問題にも当てはまりません。
私が持っているList
いくつかの含有する成分ListItem
を選択してハイライト表示され、それらのうちの1つを有する、秒。 別のListItem
をクリックして選択して強調表示すると、リスト全体(その子を含む)が再度レンダリングされます。
この問題は前のコメントとまったく同じように見えるかもしれませんが、そうではありません。 少なくとも私はそう思う。
Reactプロファイラーの結果を見てみましょう。
ご覧のとおり、画像のトップレベルにMyList
コンポーネントがあります。 このコンポーネントは、MUI List
ラッパーにすぎず、純粋になります。つまり、次のようになります。
class MyList extends React.PureComponent {
render() {
return (
<List>
{this.props.children}
</List>
);
}
}
彼のコメントの1つで@ eps1lonがList
を再レンダリングするとコンテキストが更新され、このコンテキストの更新によりすべてのコンシューマー( 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最小限の複製例を作成
MyList
( MyComponent
と呼びます)をレンダリングするコンポーネントが再レンダリングされるため、 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番目のリスト項目をクリックした後):
ご覧のとおり、期待どおりに機能します。つまり、追加の再レンダリングはありません( 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を提出しました。
List
コンポーネントが十分に大きい場合は、最適化されていても、パフォーマンスのボトルネックになる可能性があります。 Table
ドキュメントにあるような、 List
コンポーネントでのreact-window
またはreact-virtualized
の使用法を示す例を提供するべきではありませんか?Table docsにあるような、react-windowまたはreact-virtualized with Listコンポーネントの使用法を示す例を提供するべきではありませんか?
それは素晴らしいことです:+1:
Fwiwチャットアプリを作成しましたが、アプリは連絡先の大規模なリストをレンダリングする必要があります。 私は同じ問題に遭遇しました
@ mkermani144が持っていた。
@ 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を返します。
これにより、すべての子コンポーネント(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
ケース2(ユーザーがダイアログの[注文に追加]ボタンをクリックする)
レンダリング時間:356ms
私はここで初心者の間違いを犯していると確信しているので、どんなガイダンスも大歓迎です。
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で;
<Box>
esなし:
@mankittensスタイルエンジンとしてstyled-componentsを使用して、Boxコンポーネントを保持できます。 パフォーマンスははるかに良くなります。 近い将来、JSSで改善されるはずですhttps://github.com/mui-org/material-ui/pull/16858。
この号を締めくくります。 包括的なスレッドではなく、改善の可能性のある領域ごとに専用のパフォーマンスレポートが必要です。
最も参考になるコメント
優れた開発者としての@oliviertassinari 、このようなものをどのように書くことができますか? なぜあなたは_あなたの個人的な仮定_を議論として使用し、事実を使用し事実と_no_仮定。
あなたのためだけに私は10個のボタンに減らします。 10! これは、material-uiの10個のコンポーネント(さらに悪い場合:コンテナー)が、アプリが使用できなくなるまでアプリ全体の速度を低下させることを意味します。 スロットルなしのクロスウォーク21 /クローム51を備えた実際のデバイスでテスト済み:
通常のボタン:
PureButton:
これはまだ8倍の改善です! 巨大です! モバイルデバイスでこれがどれほど重要か想像できますか?
最も単純なコンポーネントの1つであるため、Buttonを使用しました。 これは、パフォーマンスの観点から、material-uiが壊れていることを示しています。 ここで、AppBar、ツールバー、リスト、ドロワーなどのコンテナーコンポーネントを想像してみてください。 これはさらに悪いです! すべてのページで20個のコンポーネント/コンテナにすばやくアクセスできます。 また、強力なデスクトップ/ Macで速度が低下しないため、material-uiが信じられないほど遅いわけではありません。
codesandboxの例を見せてください。 なぜこれが起こるべきなのか分かりません。 確かに、これはreactコンポーネントの誤った使用でのみ発生します。 そして公式の例は、react-intlが適用されていないコンテキストサブスクライバーのように見えるため、間違った使用法を示しています。 しかし、パフォーマンスを維持するための反応ガイドラインとベストプラクティスに沿った他の多くのコンポーネントがあります。
ところで:WithStylesは、モバイルデバイスのボタンに最大2,27ミリ秒を消費します。 8つのコンポーネントと60fps未満のあなた。