tower / commit
perf(exterior): defer Three.js so the map does not pay for it
src/ui/app.ts | 10 +++++++--- vite.config.ts | 8 +++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/ui/app.ts b/src/ui/app.ts index 7d89b85..25bcac5 100644 --- a/src/ui/app.ts +++ b/src/ui/app.ts @@ -15,7 +15,7 @@ import { import { KIND_COLOR } from '../views/palette.js' import { WorldStore, backupFilename, download } from '../world/store.js' import { TowerMap } from '../views/map.js' -import { ExteriorView } from '../views/exterior.js' +import type { ExteriorView } from '../views/exterior.js' import { SPIN_PERIOD_S } from '../canon/constants.js' type Tool = 'select' | PlaceKind @@ -97,7 +97,7 @@ export class App { private buildViews(): void { for (const b of document.querySelectorAll<HTMLButtonElement>('#views button')) { - b.onclick = () => this.setView(b.dataset['view'] as ViewName) + b.onclick = () => { void this.setView(b.dataset['view'] as ViewName) } } const spin = $<HTMLInputElement>('f-spin') const speed = $<HTMLInputElement>('f-speed') @@ -120,7 +120,7 @@ export class App { * animation loop, neither of which a reader who only ever opens the map should pay * for. */ - private setView(view: ViewName): void { + private async setView(view: ViewName): Promise<void> { this.view = view const isMap = view === 'map' $('map').classList.toggle('hidden', isMap ? false : true) @@ -137,6 +137,10 @@ export class App { return } if (!this.exterior) { + // Genuinely deferred: Three.js is by far the largest dependency, and a static + // import would bundle it into the entry chunk whether or not this view is + // ever opened. Vite splits it into its own chunk on this await. + const { ExteriorView } = await import('../views/exterior.js') this.exterior = new ExteriorView($<HTMLCanvasElement>('exterior'), { onPickPoint: (p) => { this.map.probe = { from: p, to: null } diff --git a/vite.config.ts b/vite.config.ts index 852945a..10d1b0e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -21,5 +21,11 @@ export default defineConfig({ // `mv dist public` does not rename when `public/` already exists, it moves dist // *inside* it. The collision is silent and produces a 404 at the site root. publicDir: 'static', - build: { target: 'es2022' }, + build: { + target: 'es2022', + // Three.js is ~515 kB and is deliberately split into its own chunk, loaded + // only when the exterior view is opened. Raise the threshold past it so the + // warning stays meaningful for chunks that are large by accident. + chunkSizeWarningLimit: 600, + }, })