12周年記念パーティ開催! 2024/5/10(金) 19:00

開発ツールVite 第1回 開発時ビルドの高速化と本環境向けの施策

Viteは、大規模プロジェクトにおける開発時のビルドに時間がかかるという、既存のツールが抱える問題を解消してくれます。その背景にはどのようなことがあるのか、どのように問題を解消しているのかを見てみましょう。

発行

著者 藤田 智朗 フロントエンド・エンジニア
開発ツールVite シリーズの記事一覧

はじめに

Viteはフロントエンド開発ツールの一つで、作者はVue.jsと同じEvan You氏です。第1回目はViteが作られた理由やその背景、そしてViteが解決する問題とその方法について解説します。

「V」から始まる名前と、Vue.jsと同じ作者ということでViteはVue.js専用のツールと思われるかもしれませんが、特定のライブラリやフレームワークに限定されるものではありません。

Viteのようなツールが作られた背景には昨今のフロントエンドの開発環境が抱えるある問題があります。 まずは、その問題を確認し、Viteはそれをどのように解決するのかを説明します。

Viteの発音

日本語公式サイトによると、Viteは「フランス語で『素早い』という意味の単語でヴィートのように発音」とあります。

昨今のフロントエンド開発環境の問題

昨今のフロントエンドの開発環境が抱える問題とは、webpackRollupParcelなどの、いわゆるバンドラツールにまつわる問題になります。

著者は以前CodeGridのwebpackの記事で、webpackが誕生した背景にフロントエンドの役割が増えるととともにコード量も増大したことに起因しているということを説明しました。

webpackの記事

その後もフロントエンドのコード量は増加の一途をたどり、開発者にとってどうしても看過できない新たな問題が浮上してきました。それは、

  • 開発中にコードを変更した際のビルド・ブラウザ反映までの時間が長い

というものです。プロジェクトの規模が大きくなければなるほどコード量も増え、この待ち時間も増えることになります。筆者も経験があるのですが、毎回コードを追加・編集してブラウザで確認しようとするたびに数秒待たされるというのは耐え難いストレスになります。

数秒でも結構なストレスですが、大規模開発になると数十秒、または1分近く待つことになるという事例もあるそうです。

いわゆる「開発者体験」に与える悪影響が見過ごせないレベルになってしまったのです。これはHot Module Replacementでも解決できない問題となっています。他にも開発用サーバーの起動に時間が掛かるという点も問題になっています。

Viteはこういった開発者体験を損なう問題を解決するために作られたツールです。では、Viteはバンドラーによるこのような問題をどのように解決するのかを解説してみましょう。

開発時のビルド速度の対策

Viteは開発時には主に下記のことをすることで、速度アップを図ります。

  • 基本的にバンドルをしない
  • esbuildを使用する

それぞれ詳細を順に説明していきます。

基本的にバンドルをしない

バンドルとはwebpackなどのバンドラーがモジュールの依存関係を解析し、その解析結果をもとにして異なる復数のモジュールからブラウザで実行可能な1つ以上のファイルを生成することを指します。

最近のフロントエンド開発では、モジュールやライブラリを使用する際は先頭にimport文などのモジュール読み込みの指定を書くことが一般的になっています。webpackなどのバンドラーは、ビルド時にはこのようなモジュールの関係性を示すimport文などを辿ってファイル間の関係性を解析し、ファイル同士を連結したり場合によっては分割するなどのバンドル処理を実行しています。

しかし、Viteは開発時のビルドではこのようなバンドル処理を基本的には行いません。最近のモダンブラウザはimport文をサポートしているので、Viteはこれを最大限活用してimport処理をブラウザに任せてしまっているのです。

とはいえ、ブラウザネイティブのES modulesでは対象となるモジュールファイルのパスは、相対または絶対パスである必要があります。そこでViteは次のようにパスを書き換えます。

Viteによるimportのパスの書き換え

import { foo } from "foo"

import { foo } from "/node_modules/.vite/foo";

このため開発時は/node_modules/.viteから始まるパスに対してHTTPリクエストが送信されることになりますが、開発用サーバーがViteの機能の一部として含まれており、この開発用サーバーがモジュールのレスポンスを返すように処理してくれます。

依存関係の事前バンドル

「基本的に」バンドルしないと説明したように、どうしてもバンドルが必要なものがあります。 package.jsonに指定される依存関係にあるライブラリやフレームワークは、主に次の2つの目的のためにViteによって事前にバンドルされます。

  • CommonJS、UMDの文法をES modulesの文法に変換する
  • 開発で使用するブラウザのパフォーマンス低下を防ぐために、複数のモジュールファイルを単一のファイルにしたり、相互インポートする別々のファイルに出力する

1つ目については、たとえば使用するライブラリでCommonJSの文法が使われていた場合は、ブラウザが解釈可能なES modulesの文法に変換する必要があります。

2つ目については、この対策をしておくことでimportによる大量のHTTPリクエストが発生してしまうことを防ぎます。 フレームワークやラブラリによってはimportによる依存関係のネストが深かったり、1つのファイルに大量のimportが指定されていることも少なくないため、何もしなければ実行環境であるブラウザに過大な負荷を掛けてしまう可能性があります。

たとえば、lodash-esでは最初にimportされるファイル内ですでに300以上のimport文(厳密にはimportexportの両方の処理を併せ持つexport from文)が指定されており、この対策がなければ600以上のHTTPリクエストが発生することになります。公式ガイドでも例として挙げられていますので、詳しく確認したい方は参照してみてください。

開発環境下におけるローカルファイルへのアクセスだとしても、さすがにこの量のHTTPアクセスがあってはブラウザのパフォーマンス低下の影響は避けられません。 このような理由から、Viteは依存関係に対して事前にバンドル処理を行います。

また、これらのバンドルファイルは、node_modules/.viteのディレクトリ内に出力され、依存関係やバージョンに変更がない限りはこの事前バンドルもスキップされます。

esbuildの利用

Viteは、JSX(TSX)やTypeScriptなどのトランスパイル、そして前述の事前バンドルにesbuildを利用します。

esbuildはビルドが高速であることが特徴的なバンドラーです。既存のバンドラーと比較して10倍〜100倍速いと言われています。esbuildについてはCodeGridでも取り上げていますので、なぜそんなに速いのかやその他詳細を知りたい方は下記を参照してください。

また、esbuildはTypeScriptのトランスパイルはするものの型チェックは行いません。そのため、必然的にViteも型チェックを行わないため、TypeScript利用時のビルド時間短縮につながっています。

最近ではエディタやIDEが非常に高機能になっており、コードの編集中に型にエラーがあれば即座に問題を指摘してくれますので、Viteは開発時の型チェックは代わりにこちらを頼るように推奨しています。

【ワンポイント】tscによる型チェックの実行

開発時の型チェックはエディタに任せるということを先ほど説明しましたが、開発時にエディタが型についてのエラーや警告を表示したところで、ビルド自体は通ってしまいます。

またエディタで開いているページの型エラーについては気付けるかもしれませんが、一括置換や多数のファイルに影響するリファクタリングでは型エラーを見過ごしてしまうことがあるかもしれません。

そのまま本環境にデプロイされてしまうことを防ぐために、create-viteで開発環境を構築するとpackage.jsonbuildのscriptが次のように出力されます。(create-viteはViteを使ったプロジェクトの開発環境を素早く構築するためのツールです。詳細については次回解説します)

Viteが出力するbuildのscript

"build": "tsc && vite build",

このため本環境向けビルドが実行される前に、TypeScriptのcliコマンドであるtscが実行されてプロジェクト全体の型チェックが行われます。

create-viteによって同様に出力されるtsconfig.jsonnoEmitオプションはtrueに指定されているので、tsc --noEmitとして実行した場合と同じく、ファイル出力をせずに型チェックだけを行います。

esbuildのTypeScriptの説明でも、esbuildは型チェックはしないので並行してtsc --noEmitを実行することが推奨されています。

Viteが開発時にどのようにビルドを高速化しているか、主だった理由を説明しました。次の節ではViteによる本環境向けのビルドについて解説します。

本環境向けビルドの対策

開発時は速度最優先のために、思い切った方針がとられていることがおわかりいただけたかと思います。 開発時は開発者体験を優先したビルド処理をすれば良いのですが、本環境向けには一般的にはユーザー体験を優先したビルド処理をする必要があります。

Viteは本環境向けのビルドには割り切ってRollupを使用するという方針をとっています。

なぜ、Viteは開発時と同様に本環境向けにもブラウザネイティブのESモジュールとesbuildを利用しないでRollupを使用するのでしょうか? その理由について説明します。

ブラウザネイティブのES modulesの問題

ブラウザネイティブでのES modulesはほとんどのモダンブラウザに対応しているので、本環境時でも同様に利用しても問題ないように思えるかもしれませんが、残念ながらそういうわけにはいきません。

開発時はローカル上にファイルがあるため問題になりませんが、本環境ではimportによるモジュールファイル取得のためにネットワーク上を何度も往復することになり、パフォーマンスが著しく低下します。これはHTTP/2を使っていたとしても解決できないと言われています。

そのため、結局既存のバンドラーと同様にパフォーマンスを優先したバンドル処理が必要になってしまいます。

esbuildの問題

本環境向けにバンドルが必要なのであれば、Rollupではなくesbuildでもよさそうなものですが、これについてもそういうわけにはいきません。

esbuildは2022年1月現在も試験的なビルドツールという位置づけであり、特にコード分割に対応していないという点で本環境向けのビルドには不向きと言えます。

コード分割をしないと、たとえばそのページに不必要な大量のJavaScriptのコードを読み込むことになり、パフォーマンスが著しく低下してしまいます。

webpackのみならず、現在ではRollupやParcelもコード分割に対応しており、esbuildもロードマップの先頭にコード分割を挙げていることからもわかるように、コード分割はフロントエンドのビルドに欠かせない機能となっています。

このような事情から、Viteは本環境向けのビルドにはRollupを使用するという方針をとっています。

まとめ

第1回目はViteの概要や最大の特徴である開発時ビルドの高速化、そして本環境向けにはRollupを利用することについて解説しました。

次回は、その他のViteの特徴や機能の詳細について解説する予定です。