toggle Engineer Blog

トグルホールディングス株式会社のエンジニアブログでは、私たちの技術的な挑戦やプロジェクトの裏側、チームの取り組みをシェアします。

インポートの変更でvite devが数分から数秒になった

こんにちは。トグルホールディングス、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を何倍インポートしてたら遅くなるはず

graph TD;
    mapbox-gl-->A;
    mapbox-gl-->B;
    mapbox-gl-->C;
    A-->routes;
    B-->routes;
    C-->routes;
// 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.aliasimport mapboxgl from '@/lib/mapboxgl'

をやる時もimport mapboxgl from 'mapbox-gl'みたいな速度、数回読まれてる。

分かったこと

  • HonoXが使ってるviteのバージョンか設定がバグっている
  • ファイルのまとめ方によってビルド時間が変わる
  • インポートのやり方によってビルド時間が変わる

今度ビルドが遅くなった時インポートのやり方を変えるべきかもしれないです。 また、viteのバグか、HonoXのみの問題かはまた別の記事に記載したいと思います!