React で何かをサクッと作るときによく Material UI React (以下 MUI) を使うのですが、ビルド速度が非常に遅くなるな〜 でもこんなものなのかな〜と思っていたのですが、調べてみたら MUI 特有の問題があるらしくそれについての検証をしてみました。
発生した問題
まず、僕の開発環境ですが以下のような感じです。
- mac
- VSCode
- TypeScript
- React
npx create-react-app app --template typescrip
で作成
- MUI
npm install @mui/material @emotion/react @emotion/styled
- その他いろいろなライブラリ
このような環境下で開発をしていると以下のような問題が発生しました。
npm start
して変更の watch → ビルド → hot reload しているとなかなかビルドが終わらずに変更が反映されない- VSCode の自動補完の表示に数秒〜10秒程度かかる
これらは開発の体験をかなり悪くするのでなんとかしたいと思っていました。
しかしながら Web フロントエンドの開発ツールにいろいろなものを利用していて何が影響して重くなっているのかはよくわかりませんでした。
気が付き
MUI ぐらいしか入れていない小さい構成のプロジェクトでも遅くなることに気が付きました。
そこで vscode material ui react slow
みたいな感じでググったところ以下の issue にたどり着きました。
他にも Stack Overflow などでも同様の報告がチラホラありました。この時点で重くなっている原因は MUI にありそうだなと判断しました。
そして、いくつかの記事で import の書き方についての言及を見かけました(以下は一例)。
以下のような {}
を使った import ではなく、/Box
のように指定する方法に置き換えるというものです(この形式の import をなんと呼ぶのかは知らない...)。
// こうじゃなくて import { Box } from "@mui/material";
// こうやる import Box from "@mui/material/Box";
検証
import 文の違いでどのぐらいの速度差が出るのかを検証しました。
今回の検証に使用したプロジェクトは GitHub にあります。
https://github.com/mironal/mui-time-sample
import 形式の違いは commit で分けていて以下のコミットで diff を見ることができます。
import 改善 · mironal/mui-time-sample@76c1a49 · GitHub
コードの殆どは App.tsx
に集約されていて、これを npm run build
して終了までの時間を time
で計測します。
import { } from "@mui/material";
形式
App.tsx
は以下のとおりです。
import "./App.css"; import { Box, CircularProgress, Container, createTheme, Typography, } from "@mui/material"; import { ThemeProvider } from "@emotion/react"; import { green, purple } from "@mui/material/colors"; const theme = createTheme({ palette: { primary: { main: purple[500], }, secondary: { main: green[500], }, }, }); function App() { return ( <ThemeProvider theme={theme}> <Container> <Typography>Test</Typography> <Box> <CircularProgress /> </Box> </Container> </ThemeProvider> ); } export default App;
\time npm run build
の結果
- 5.31 real 9.22 user 1.15 sys
- 5.39 real 9.12 user 1.17 sys
- 5.20 real 9.11 user 1.14 sys
- real 平均: 5.3 sec
ビルドされたファイルサイズ
63.5 KB build/static/js/2.d242541a.chunk.js 1.62 KB build/static/js/3.448db786.chunk.js 1.16 KB build/static/js/runtime-main.ed10380a.js 556 B build/static/css/main.a617e044.chunk.css 512 B build/static/js/main.4025fc07.chunk.js
import Box from "@mui/material/Box"; 形式
import の部分だけを以下のように変更しました。
import Box from "@mui/material/Box"; import CircularProgress from "@mui/material/CircularProgress"; import Container from "@mui/material/Container"; import Typography from "@mui/material/Typography"; import green from "@mui/material/colors/green"; import purple from "@mui/material/colors/purple"; import createTheme from "@mui/system/createTheme";
\time npm run build
の結果
- 4.21 real 7.75 user 0.88 sys
- 4.39 real 7.76 user 0.92 sys
- 4.13 real 7.77 user 0.87 sys
- real 平均: 4.24 sec
ビルドされたファイルサイズ
63.44 KB build/static/js/2.b8e891a1.chunk.js 1.62 KB build/static/js/3.85251bc1.chunk.js 1.16 KB build/static/js/runtime-main.7329e70a.js 556 B build/static/css/main.a617e044.chunk.css 509 B build/static/js/main.14a36591.chunk.js
結果
ビルドにかかる時間は import Box from "@mui/material/Box";
の形式で import したほうが約1秒早くなる結果になりました。
計測はしていませんが、この規模だと VSCode の補完が実感できるほど遅くなることはありませんでした。
まとめ
import Box from "@mui/material/Box";
形式での import のほうがビルド時間が早くなるのでおすすめです。
今回は非常に小さいコードベースでの検証でしたが、規模が大きくなるとさも大きくなると思います。
とは言え、 VSCode の自動 import だと import { Box} from "@mui/material";
形式になるので毎回手動で修正するのは面倒かと思います。
また、実際にこのルールを運用するとなると lint の rule やこの形式に合わせた自動 import を助けるツール(Extension)などが欲しくなると思います。