こんにちは。トグルホールディングス、AIエンジニアのマーカスです。
トグルホールディングスエンジニアアドベントカレンダーの23日目の記事です!
概要
- import mapboxgl from 'mapbox-gl'
+ import mapboxgl from '../lib/mapboxgl'
っていう謎の変更でvite dev
のビルド時間が2分から18秒までに短縮したについての話です。
背景
開発する途中で動作確認したいのはよくある例で、そこでJavaScriptツールでHMRという機能で変更点をすぐ見えるようになったはずなのに、何故か2分かかったのは謎です。また、vite build
を試したとき15秒しかかからないのはもっと謎です。
そこでgit bisect
で悪いコミットを探し、インポートの変更を見つけました。
理由を知らないまま進むと今後同じことを起きるかもしれないため、徹底的に調査しました。
推測
TypeScriptの型検査でビルド時間かかるのはよくある話。
そこでmapbox-glのインポートを一つのファイル(../lib/mapboxgl.ts
)にまとめて、型検査を1回でしか実行しないようにした、のが推測です。それを再現するために小さなプロジェクトを建ててみました。
(※ TypeScriptとは関係ないのビルドツール(rollupなど)が遅いの可能性もあるが、一旦ここで放っておきます)
// ../lib/mapboxgl.ts
import mapboxgl from 'mapbox-gl'
// mapboxglの処理
export default mapboxgl
再現
プロジェクトを建てる
発見したプロジェクトがHonoXを使ってるから同じものにしました。
pnpm create hono
# ? Target directory my-app
# ? Which template do you want to use? x-basic
# ? Do you want to install project dependencies? yes
# ? Which package manager do you want to use? pnpm
ディレクトリの構造
my-app
├── app
| ├── client.ts
| ├── global.d.ts
| ├── islands
| | └── counter.tsx
| ├── routes
| | ├── _404.tsx
| | ├── _error.tsx
| | ├── _renderer.tsx
| | └── index.tsx
| └── server.ts
├── package.json
├── pnpm-lock.yaml
├── public
| └── favicon.ico
├── tsconfig.json
├── vite.config.ts
└── wrangler.toml
再現するファイルを作る
pnpm add mapbox-gl
// app/routes/index.tsx
import { createRoute } from 'honox/factory'
import Slow from '../islands/Slow'
export default createRoute((c) => {
return c.render(<Slow />)
})
// app/islands/Slow.tsx
import mapboxgl from 'mapbox-gl'
export default function Slow() {
const dummy = typeof mapboxgl
return (
<div>
{dummy}
</div>
)
}
my-app
├── app
| ├── client.ts
| ├── global.d.ts
| ├── islands
| | └── Slow.tsx
| ├── routes
| | ├── _404.tsx
| | ├── _error.tsx
| | ├── _renderer.tsx
| | └── index.tsx
| └── server.ts
├── package.json
├── pnpm-lock.yaml
├── public
| └── favicon.ico
├── tsconfig.json
├── vite.config.ts
└── wrangler.toml
よし!プロジェクトを建てられたので早速実験を実行する。
初期ビルド速度を試す
pnpm vite dev
curl -o /dev/null -w "%{time_total} seconds\n" -s http://localhost:5173
# 3.998502 seconds
わざと遅くさせる
先ほどちょっと言ったが、mapbox-glを何倍インポートしてたら遅くなるはず
// app/islands/Slow.tsx
import mapboxgl from 'mapbox-gl'
import mapboxgl2 from 'mapbox-gl'
import mapboxgl3 from 'mapbox-gl'
import mapboxgl4 from 'mapbox-gl'
import mapboxgl5 from 'mapbox-gl'
import mapboxgl6 from 'mapbox-gl'
import mapboxgl7 from 'mapbox-gl'
import mapboxgl8 from 'mapbox-gl'
import mapboxgl9 from 'mapbox-gl'
import mapboxgl10 from 'mapbox-gl'
export default function Slow() {
const dummy = typeof mapboxgl ||
typeof mapboxgl2 ||
typeof mapboxgl3 ||
typeof mapboxgl4 ||
typeof mapboxgl5 ||
typeof mapboxgl6 ||
typeof mapboxgl7 ||
typeof mapboxgl8 ||
typeof mapboxgl9 ||
typeof mapboxgl10
return (
<div>
{dummy}
</div>
)
}
pnpm vite dev # キャッシュを使わないように再起動(速度を測る前に実行するが今後略する)
curl -o /dev/null -w "%{time_total} seconds\n" -s http://localhost:5173
# 30.474034 seconds
よし!遅くなった。
修正
仮説から
ここで前の仮説からやってみよう。
// app/islands/Slow.tsx
import mapboxgl from '../lib/mapboxgl'
import mapboxgl2 from '../lib/mapboxgl'
import mapboxgl3 from '../lib/mapboxgl'
import mapboxgl4 from '../lib/mapboxgl'
import mapboxgl5 from '../lib/mapboxgl'
import mapboxgl6 from '../lib/mapboxgl'
import mapboxgl7 from '../lib/mapboxgl'
import mapboxgl8 from '../lib/mapboxgl'
import mapboxgl9 from '../lib/mapboxgl'
import mapboxgl10 from '../lib/mapboxgl'
export default function Slow() {
const dummy = typeof mapboxgl ||
typeof mapboxgl2 ||
typeof mapboxgl3 ||
typeof mapboxgl4 ||
typeof mapboxgl5 ||
typeof mapboxgl6 ||
typeof mapboxgl7 ||
typeof mapboxgl8 ||
typeof mapboxgl9 ||
typeof mapboxgl10
return (
<div>
{dummy}
</div>
)
}
curl -o /dev/null -w "%{time_total} seconds\n" -s http://localhost:5173
# 17.119245 seconds
あれ?ちょっと早くなったがインポート1回みたいの速度じゃないな・・・
../lib/mapboxgl
を1回インポートしてやってみよう
// app/islands/Slow.tsx
import mapboxgl from '../lib/mapboxgl'
export default function Slow() {
const dummy = typeof mapboxgl
return (
<div>
{dummy}
</div>
)
}
curl -o /dev/null -w "%{time_total} seconds\n" -s http://localhost:5173
# 4.015881 seconds
うん。mapbox-glがまだ数回読まれてる。
前のディレクトリ構造から
色んな試行錯誤してroutesのディレクトリ構造が原因なのは分かったんでここで試してみる。
作り直し
// app/routes/foo/index.tsx
import { createRoute } from 'honox/factory'
import Slow from '../../islands/Slow'
export default createRoute((c) => {
return c.render(<Slow />)
})
// app/islands/Slow.tsx
import mapboxgl from 'mapbox-gl'
export default function Slow() {
const dummy = typeof mapboxgl
return (
<div>
{dummy}
</div>
)
}
my-app
├── app
| ├── client.ts
| ├── global.d.ts
| ├── islands
| | └── Slow.tsx
| ├── lib
| | └── mapboxgl.ts
| ├── routes
| | ├── _404.tsx
| | ├── _error.tsx
| | ├── _renderer.tsx
| | └── foo
| | └── index.tsx
| └── server.ts
├── package.json
├── pnpm-lock.yaml
├── public
| └── favicon.ico
├── tsconfig.json
├── vite.config.ts
└── wrangler.toml
curl -o /dev/null -w "%{time_total} seconds\n" -s http://localhost:5173/foo
# 3.860002 seconds
よし!また4秒
遅くさせる(2回目)
// app/islands/Slow.tsx
import mapboxgl from 'mapbox-gl'
import mapboxgl2 from 'mapbox-gl'
import mapboxgl3 from 'mapbox-gl'
import mapboxgl4 from 'mapbox-gl'
import mapboxgl5 from 'mapbox-gl'
import mapboxgl6 from 'mapbox-gl'
import mapboxgl7 from 'mapbox-gl'
import mapboxgl8 from 'mapbox-gl'
import mapboxgl9 from 'mapbox-gl'
import mapboxgl10 from 'mapbox-gl'
export default function Slow() {
const dummy = typeof mapboxgl ||
typeof mapboxgl2 ||
typeof mapboxgl3 ||
typeof mapboxgl4 ||
typeof mapboxgl5 ||
typeof mapboxgl6 ||
typeof mapboxgl7 ||
typeof mapboxgl8 ||
typeof mapboxgl9 ||
typeof mapboxgl10
return (
<div>
{dummy}
</div>
)
}
curl -o /dev/null -w "%{time_total} seconds\n" -s http://localhost:5173/foo
# 29.796043 seconds
また30秒
修正
// app/islands/Slow.tsx
import mapboxgl from '../lib/mapboxgl'
import mapboxgl2 from '../lib/mapboxgl'
import mapboxgl3 from '../lib/mapboxgl'
import mapboxgl4 from '../lib/mapboxgl'
import mapboxgl5 from '../lib/mapboxgl'
import mapboxgl6 from '../lib/mapboxgl'
import mapboxgl7 from '../lib/mapboxgl'
import mapboxgl8 from '../lib/mapboxgl'
import mapboxgl9 from '../lib/mapboxgl'
import mapboxgl10 from '../lib/mapboxgl'
export default function Slow() {
const dummy = typeof mapboxgl ||
typeof mapboxgl2 ||
typeof mapboxgl3 ||
typeof mapboxgl4 ||
typeof mapboxgl5 ||
typeof mapboxgl6 ||
typeof mapboxgl7 ||
typeof mapboxgl8 ||
typeof mapboxgl9 ||
typeof mapboxgl10
return (
<div>
{dummy}
</div>
)
}
curl -o /dev/null -w "%{time_total} seconds\n" -s http://localhost:5173/foo
# 2.516782 seconds
4秒すらかからなかった!
余談
import mapboxgl from '../lib/mapboxgl'
とは別
- 絶対パス(
import mapboxgl from '/home/my-app/app/lib/mapboxgl'
) - Viteのresolve.alias(
import mapboxgl from '@/lib/mapboxgl'
)
をやる時もimport mapboxgl from 'mapbox-gl'
みたいな速度、数回読まれてる。
分かったこと
- HonoXが使ってるviteのバージョンか設定がバグっている
- ファイルのまとめ方によってビルド時間が変わる
- インポートのやり方によってビルド時間が変わる
今度ビルドが遅くなった時インポートのやり方を変えるべきかもしれないです。
また、viteのバグか、HonoXのみの問題かはまた別の記事に記載したいと思います!