V 4からV 5への材料UIをアップグレードしている55 %のパフォーマンス改善


この記事はもともとはWavebox blog
ブラウザの大部分Wavebox , は、ユーザーインターフェイスといくつかのこの我々はMaterial-UI . 私たちは2016年から材料UIの重いユーザーで、バージョン0.14.2を使い始めました.それ以来、図書館といくつかの大きな移動に大きな変化を見ました.材料UI 5最近起動し、我々はいくつかのバージョン4からのアップグレードの経験を共有したい.
する必要があることを考え出すことから、実際に我々のCodebase全体でそれをすることはおよそ3日をとったが、そうすることで、我々は55 %のパフォーマンス改善をつかむことができました.ここでどのように我々はそれをやった.

📘 移行ガイドから始める
Migrating from v4 to v5 , あなたはここにいくつかの時間を費やすつもりですが、ガイドはすべての基本とより多くをカバーしています.ちょうどダイビングのために言われて、指示を無視して、移行ガイドからの我々の離陸は「更新されて、Codemodesを走らせました」ということです.それで私たちは.
npm install @mui/material @mui/styles @mui/lab @mui/icons-material @emotion/react @emotion/styled
npm uninstall @material-ui/core @material-ui/icons @material-ui/lab
npx @mui/codemod v5.0.0/preset-safe .
...これは私たちに出発点を与えます.すべては非コンパイルの出発点ですが、まだ出発点です.

🕶️ スタイリング
これは私たちにとって最大の仕事だった.私たちはスタイルの変更を前に材料のUIを使用すると、彼らはインラインスタイルからJSSに移動するが、この時間のUIemotion . 私たちは多くのコンポーネントのためにJSSに大いに依存してきました.
const styles = (theme) => ({
  root: {
    padding: 8,
    overflow: 'hidden'
  },
  item: {
    margin: 12,
    display: 'inline-block',
    backgroundColor: theme.palette.primary.main
  }
})

@withStyles(styles, { withTheme: true })
class MyComponent extends React.PureComponent {
  render () {
    const { className, classes, theme, ...passProps } = this.props

    return (
      <div className={clsx(className, classes.root)} {...passProps}>
        <div className={classes.item}>1</div>
        <div className={classes.item}>2</div>
        <div className={classes.item}>3</div>
      </div>
    )
  }
}
我々は少なかったoptions readily available 私たちに移行する.スタイル付きコンポーネントAPIを使用するか、システムを使用する.したがって、この単純なコンポーネントを両方に変換して、それがどのように見えるかを確認できます.

オプション1 :スタイルコンポーネントの使用
Styled components 別のコンポーネントを一連のスタイルでラップし、そこからコンポーネントをあなたの反応ツリーに追加します.一見、それは快適に身近に見えました、しかし、これは現在スタイリングのための遺産解決で、反応と互換性がありません.StrictModeまたは反応18我々はいくつかの時点でこれを再訪する必要があることを意味します.
const Root = styled('div')(
  ({ theme }) => `
  padding: 8px;
  overflow: hidden;
`);
const Item = styled('div')(
  ({ theme }) => `
  margin: 12px;
  display: inline-block;
  background-color: ${theme.palette.primary.main};
`);
class MyComponent extends React.PureComponent {
  render () {
    const { classes, theme, ...passProps } = this.props

    return (
      <Root {...passProps}>
        <Item>1</Item>
        <Item>2</Item>
        <Item>3</Item>
      </Root>
    )
  }
}

オプション2 :システムの使用
The system あなたのスタイルを支柱として要素と並んでインラインで定義することができます.これはほとんど反応する0.14の古いインラインスタイルに戻るように感じます、しかし、舞台裏では、賢いビットの全体の荷は起こります、彼らが再利用されることができるように、これらのスタイルはスタイルシートに置かれます.このアプローチを使用してトレードオフがあり、それはパフォーマンスです.材料UIの独自のベンチマークレンダリング1000ボックスのコンポーネントによると、スタイルのコンポーネントのための360 ms VSちょうど160 msかかります.
const Item = function (props) {
  return (
    <Box
      sx={{
        margin: 12,
        display: 'inline-block',
        backgroundColor: 'palette.primary.main'
      }}
      {...props} />
  )
}

class MyComponent extends React.PureComponent {
  render () {
    const { classes, theme, ...passProps } = this.props

    return (
      <Box
        sx={{
          padding: 8,
          overflow: 'hidden'
        }}
        {...passProps}
      >
        <Item>1</Item>
        <Item>2</Item>
        <Item>3</Item>
      </Box>
    )
  }
}

🤔 オプション3 : erm ...
さて、私はここで正直になるでしょう、我々は我々のオプションを考慮するために1分間立ち止まりました.パフォーマンスは私たちのための大きいものであり、レンダリング時間を倍増するよりも、それは過度に魅力的である何かではありません.我々は、JSSにインラインスタイルから移動するパフォーマンスの向上を覚えて十分な長さの材料のUIを使用しているし、すべてのそれらの小さなパフォーマンスのブーストは本当に違いを作る.
我々はまた、いくつかの種類のショートカットを探していた、我々は多くのコンポーネントを持っていると手で一人一人を通過しなければならない時間がかかります.マイグレーション(実行時や開発中)を自動化する簡単な方法を見ることができませんでした.
それから、我々は我々の現在のスタイルを消費することができて、我々がMUI 5と共に使うことができる何かを出力することができるように、「withstyle」を再実行しているのを見ました、しかし、これはちょうど我々が後日にピッキングしているバンド援助のように感じました.
それで、我々は図面板に戻って、他のオプションを見ました.遊んで少し後、我々は完全に異なる何かに定住.潜在的に自由なパフォーマンスブーストといくつかの半自動的な移行を与える可能性がある何か.
🤩 CSSを紹介!はい、大丈夫、少し反抗的.私たちの考えは、JSアプローチでCSS全体を落とすことによって、JavaScriptで行われる必要がある処理作業の束を落とし、途中でいくつかの無料CPUサイクルをつかむことでした.我々はすでにアプリケーションの周りのいくつかのスタイルのために使用しているので、すべてはすでに我々のビルドスクリプトでセットアップです.これはすべてのようなジャンプのように見えなかった.
少しずつ😎) スクリプティングは、我々は半自動化の大半を自動化できると思いました.私たちのスタイル定義は通常「const style =」から始まります.したがって、それらを引き抜くことはあまり難しくありません.ここでは、私たちの新しいコンポーネントのようなものです.
MyComponentモジュールです.less
.root {
  padding: 8px;
  overflow: hidden;
}
.item {
  margin: 12px;
  display: inline-block;
  background-color: "__TODO__"
}
MyComponentjs
import classes from './MyComponent.module.less'
class MyComponent extends React.PureComponent {
  render () {
    const { className, theme, ...passProps } = this.props

    return (
      <div className={clsx(className, classes.root)} {...passProps}>
        <div className={classes.item}>1</div>
        <div className={classes.item}>2</div>
        <div className={classes.item}>3</div>
      </div>
    )
  }
}
あなたが見ることができるように、あまりにも多くのジャンプ.唯一のビットが不足している背景色の厄介なテーマのプロパティは、我々はオプションのカップルここに来た.
  • プロパティを2回、JavaScriptで一度、以下を使用して
  • CSS変数を使用し、JavaScriptからドキュメントに書き込む
  • ...我々は両方を使用することを決めた.私たちはテーマのプロパティを2つのカテゴリーに分けました.変更されないもの(“パレット. primary . main”はそれらのうちの1つです、それは常に青いです)、そして、それを変更するもの(“パレット. background . paper”は暗闇や光モードのユーザーの好みに依存します).両方のアプローチを使用すると、両方の世界の最高を与える.我々のテーマプロバイダを拡張することによって、我々はかなり速くCSSに必要なものを押し出すことができました..
    class ThemeProvider extends React.PureComponent {
      constructor (props) {
        super(props)
    
        this.state = {
          theme: createTheme({ ... })
        }
        this.stylesheet = document.createElement('style')
        document.head.prepend(this.stylesheet)
      }
      render () {
        const { theme } = this.state
    
        document.body.className = `theme-${theme.palette.mode}`
        this.stylesheet.innerHTML = `
          :root {
            --theme-primary-main: ${theme.palette.primary.main};
          }
        `
    
        return (
          <StyledEngineProvider injectFirst>
            <ThemeProvider theme={theme}>
              {children}
            </ThemeProvider>
          </StyledEngineProvider>
        )
      }
    }
    
    以下の変数
    上で述べたように、変更されない色に対してより少ない変数を使用する傾向がありますが、テーマに応じてカスタマイズする必要がある場合は、ドキュメント本体からクラスを読み込むことができます.それは少しより多くのコードです、しかし、すべてはコンパイル時にセットアップです.
    @theme-primary-main-light: #0277BD;
    @theme-primary-main-dark: #4FC3F7;
    
    .item {
      :global(body.theme-light) {
        background-color: @theme-primary-main-light;
      }
      :global(body.theme-dark) {
        background-color: @theme-primary-main-dark;
      }
    }
    
    CSS変数
    色が完全に決定されないか、それが我々が変化するということを知っている何かであるならば、我々はCSS変数を使うことができます.テーマのプロバイダが起動されたら、色が設定され、コンポーネントが表示されます.
    .item {
      background-color: var(--theme-primary-main);
    }
    

    🚀🚀🚀 パフォーマンス.はい!
    として、我々はいくつかの無料のパフォーマンスを拾うことができたが、私たちは期待していなかった何を管理していた.我々は、クロムのパフォーマンスタブを使用して測定の3セットを取って、ベンチマークとして設定画面を使用します.
    マテリアルUI 4
  • CPU時間:1191 ms、1200 ms、1163 ms(平均1185 ms)
  • 閉塞時間:740 ms,859 ms,792 ms(平均797 ms)
  • ピークJSヒープ:36.1 MB、35.9 MB、36.2 MB(平均36 MB)
  • マテリアルUI 5
  • CPU時間:558 ms、758 ms、559 ms(625 ms)
  • ブロッキング時間:316 ms,447 ms,314 ms(359 ms)
  • ピークのJSヒープ: 34.5 MB、35.2 MB、34.6 MB ( 34.7 MB )
  • ええ、それはブロック時間の50 %減少です.797 msから359 msまでのダウン.その節約は祝うためにいくらかのケーキに値します🎂.
    それと同時に、CPU時間の48 %の減少とメモリのわずかな低下(1.3 MB)を見ました.これらのすべての大きい(そして小さい)改善は、Waveboxが速くてスムーズに走るのを助けます.
    我々は、この更新プログラムをロールバックするために働いているbeta channel 次の週以上のwave boxバージョン10.95.