Vite Bundler / Tree Shaking
Vite Bundler
Viteは開発モードではesbuildを利用し(高速化)、Rollupを本番ビルドに利用する(最適化)。esbuildよりもRollupのTree Shakingの方が洗練されている。
JSのESモジュールの静的な構造を利用してTree Shakingを行う。言い換えるとESモジュールでは動的な読み込みが禁止されているため、Tree Shaking が有効とも言える。
// できない
if (condition) {
import {SomeModule} from 'Hoge'
}
Tree Shakingのプロセス自体は、TypeScriptとJavaScriptで基本的には同じ。コンパイル中にTypeScriptの型情報は削除され、ESモジュールのJSが生成されるため、Tree Shaking時には型は関係ないと思われる。
僕の理解では、ワイルドカードでimport * as HOGEしたとしても、不要なモジュールは削除される。
bundle size analyzer
viteのbundle size analyzerには下記を使用する。
yarn add -D rollup-plugin-visualizer
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig(({ mode }) => {
return {
build: {
rollupOptions: {
plugins: [
visualizer(),
],
},
},
};
});
Next.jsのbarrel fileの最適化について
barrel fileとは複数のモジュールを1つのファイルでまとめてexportすること。
Rolldown
Viteが内部的に2種類のバンドラーを併用している点については、開発環境と本番環境で微妙な差異が生じるなどの欠点がある。
後継として開発されたRolldownは、RollupのAPI互換性を保ちつつも、esbuildのアーキテクチャにインスパイアされた設計を採用している。2024/3/8にオープンソース化。開発はRollupと同じEvan You。
ワイルドカードのimportについて
import { useEffect, useRef } from "react"
import * as THREE from "three"
// import { Scene, PerspectiveCamera, WebGLRenderer, BoxGeometry, MeshBasicMaterial, Mesh } from "three"
const CanvasContainer = () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mountRef = useRef<any>(null)
useEffect(() => {
if (!mountRef.current) return
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.z = 5
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
mountRef.current.appendChild(renderer.domElement)
const geometry = new THREE.BoxGeometry()
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)
renderer.render(scene, camera)
}, [])
return <div ref={mountRef} />
}
export default function App() {
return (
<div id="canvas-container">
<CanvasContainer />
</div>
)
}
viteのrollup optionsでtreeshake: falseを設定すると、three/build/three.module.jsは1.22MBあるということがわかる。treeshake: trueにして、ビルドすると798.73KBになるので、きちんとtree shakingされているようです。
ただしr3fを併用した場合、ワイルドカードを使おうが、モジュールごとにimportしようが、three/build/three.module.js のバンドルサイズは1.22MBのまま。(r3f内部でthreejsが使用されているのが、うまくtree-shakeされないのでは…?)
import { Canvas } from "@react-three/fiber"
import { Color } from "three"
const CanvasContainer = () => {
const color = new Color("red")
return (
<Canvas>
<mesh>
<boxGeometry />
<meshBasicMaterial color={color} />
</mesh>
</Canvas>
)
}
fiberのCanvasではなく、Custom Canvasを作成すると、たしかに下記のようにthreeモジュールはtree-shakingされます。ただ、createRoot(canvas).render()ということは、ReactDOMのrootの外側に作成することになるので、いつ使うんだろうという気もします。
// import React from "react"
// import ReactDOM from "react-dom/client"
// import App from "./App"
// import "./styles/index.css"
// ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
// <React.StrictMode>
// <App />
// </React.StrictMode>
// )
import { extend, createRoot } from "@react-three/fiber"
import * as THREE from "three"
// import { Mesh, BoxGeometry, MeshStandardMaterial } from "three"
extend(THREE)
// extend({ Mesh, BoxGeometry, MeshStandardMaterial })
createRoot(document.getElementById("canvas-root") as HTMLCanvasElement).render(
<>
<mesh>
<boxGeometry />
<meshStandardMaterial />
</mesh>
</>
)