MUIアイコンは@mui/icons-materialで読み込むと最大6倍遅くなる

MUIアイコンとは

MUIアイコンとはMaterial UI(MUI)で使える公式のアイコンコンポーネント群のことです。

@mui/icons-material というnpmパッケージで提供されています。

MUIをアイコンも含めて使用するにはnpmを使用して、以下のコマンドでインストールします。

npm i @mui/material @emotion/react @emotion/styled @mui/icons-material

@mui/icons-materialで読み込むと最大6倍遅くなる

MUIでは @mui/icons-material からアイコンをインポートできます。

例えば、MUIのボタンにアイコンを使用して削除ボタンを作成する場合は以下のようなコードで読み込みます。

App.tsx
import { Button } from '@mui/material'
import { Delete } from '@mui/icons-material'

function App() {
  return (
    <>
      <Button variant="contained" endIcon={<Delete />}>
        削除
      </Button>
    </>
  )
}

export default App

コードが簡潔なので、この書き方で読み込んでいるケースをよく見かけますが、MUIの公式ドキュメントには「名前付きインポートはパスベースのインポートよりも最大6倍遅くなる可能性があります。」と記載されており、非推奨となっています。

This is especially true when using @mui/icons-material, where named imports can be up to six times slower than default path-based imports:

Avoid barrel imports | Material UI

パスベースのインポートの場合のコードは以下のようになります。

バレル ( { } ) を使わずにパスから取得しています。

App.tsx
import Button from '@mui/material/Button'
import Delete from '@mui/icons-material/Delete'

function App() {
  return (
    <>
      <Button variant="contained" endIcon={<Delete />}>
        削除
      </Button>
    </>
  )
}

export default App

MUI Button @mui/icons-material sample

どちらも見た目は同じになりますが、処理時間は後者のほうが早くなります。

試しに「vite --debug transform」を実行して処理時間を比較すると、パスからインポートしている方は1ms未満ですが…

vite:transform 0.91ms /node_modules/.vite/deps/@mui_icons-material_Delete.js?v=965ac7d2
vite:transform 0.94ms /node_modules/.vite/deps/@mui_material_Button.js?v=965ac7d2

名前付きインポートのほうはどちらも直接パスで指定していないため、時間がかかっていることが確認できます。

vite:transform 41.88ms /node_modules/.vite/deps/@mui_material.js?v=965ac7d2
vite:transform 201.09ms /node_modules/.vite/deps/@mui_icons-material.js?v=965ac7d2

ボタンよりもアイコンのほうが処理時間がかかっているのは数が多いからです。

MUI以外でも名前付きインポートは避けるべき

この記事ではMUIを例に説明しましたが、lodashなどのライブラリでもパスベースでなければ処理時間がかかってしまうので、パスベースを使用するようにしたほうが良いです。

import { uniq } from 'lodash'  // ❌
import uniq from 'lodash/uniq' // ✅️

ただし、ライブラリによっては「import format from 'date-fns/format'」のような形式で読み込めないこともあるので注意が必要です。(例: date-fns)

import format from 'date-fns/format'     // ❌
import { format } from 'date-fns/format' // ✅️