Viteに移行してみたら開発環境が快適になった

2025-03-13

ずっと以前からGulpなどやWebpackを使ってブラウザ上で動くウェブアプリを作っていたが、Viteに載せ替えてみた。

タスクランナー小史

以前のGulp+browserify(後にWebpackに変更)でなにをしたかったかというと、複数のファイルに分割したJavaScriptのコードをひとまとめにして、ブラウザ上で動かせるようにするのが目的だった。 さらには変数名を縮めてサイズを減らしたり、SASSやTypeScriptを扱ったり、ソースの更新を見張って自動的に再コンパイル+lintでコードスタイルチェック+自動ページリロードで反映されるようにしていた。

時は流れて知らぬうちに環境がより一層整備されていたらしく、Viteというのが結構使われているっぽい(観測範囲の限り)ので、どんなもんかとみてみた。

Viteかんそう

Viteがなんたるかはオフィシャルにあたってもらうとして、試した利点として、

  • 設定なしでもほとんどそのまま使える
    • index.html からスクリプトタグで読み込むソースを自動的にパックしてくれる
    • 最初からTypeScriptが使える
    • SASSも?
  • 開発用ローカルサーバの機能がついてる
  • 再コンパイル、ページリロードが速い

個別ユースケース

vite.configをモジュールにする

Viteの設定ファイルをvite.config.ts(または.js)というファイル名にすると怒られる:

The CJS build of Vite’s Node API is deprecated. See https://vite.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.

ので、.mts(または.mjs)にすること。

アセットの出力ファイル名指定

リリース用にvite buildでファイルを出力する際に、htmlから読み込まれるJavaScriptやCSSなどのアセットファイル名に自動的にハッシュが追加されてしまう。 キャッシュ効果防止にはいいかもしれないが、ファイル名を指定したい場合にはconfigのbuild.rollupOptions.outputに記述:

export default defineConfig({
build: {
rollupOptions: {
output: {
entryFileNames: 'assets/[name].js',
assetFileNames: 'assets/[name].[ext]',
},
},
},
worker: {
rollupOptions: {
output: {
entryFileNames: 'assets/[name].js',
assetFileNames: 'assets/[name].[ext]',
},
},
},
}

起点となるhtmlのminify

index.htmlなどブラウザから読み込むhtmlがそのままではminifyされないので、するにはnpmで必要なモジュールをインストールして:

$ npm install --save-dev vite-plugin-html-minifier
$ npm install --save-dev @types/html-minifier # TypeScriptの場合、こちらも

プラグインに追加する:

// vite.config.mts
import { defineConfig } from 'vite'
import htmlMinifier from 'vite-plugin-html-minifier'

export default defineConfig({
plugins: [
htmlMinifier({ // ← これ
minify: true,
}),
],
})
  • JavaScriptやCSSは自動的にminifyされるっぽい

JavaScript内でhtmlファイルの内容を文字列としてインポート

JavaScript内でhtmlを書き換える場合に、内容を別のhtmlファイルに記述しておいてそれを埋め込みたい場合にどうするかというと、インポートに?inlineと指定する:

import aboutHtmlContent from '../res/about.html?inline'

let root: HTMLElement
root.innerHTML = aboutHtmlContent

インポートされるhtmlから参照される画像の埋め込み

前述のようにインポートしたhtml内から参照する画像ファイルを埋め込みたい場合にどうするか。 結局事前に埋め込むやり方は分からず、同じくJavaScriptで画像を?rawインポートしてセットするしかなかった:

import githubLogoSvg from '../res/github-logo.svg?raw'

let root: HTMLElement
const img = root.querySelector('img#github-logo')
if (img != null) {
img.setAttribute('src', `data:image/svg+xml;base64,${btoa(githubLogoSvg)}`)
}

WebWorker, AudioWorklet

WebWorkerやAudioWorkletなどの、メインスレッド以外のワーカースレッドで動かすスクリプトは別ファイルにする必要があるが、どうするか。 htmlからscriptタグで読み込まれるスクリプトはアセットとして自動的にビルドされるが、WebWorkerはJavaScript内からnew Worker('パス')で起動されるため、自動的にはビルドされない。

どうするかというとインポート時に?worker&urlと指定して、それをURLとして使用する:

import DmcWorkletURL from '../../dmc_channel_worker.ts?worker&url'

let context: AudioContext

context.audioWorklet.addModule(DmcWorkletURL)

かんそう

GulpとWebpackでは設定ファイルを大量にゴリゴリ書く必要があったのがほぼなくなって、正直環境の進化に感心している。

リンク