Vite

Viteについて

始めに
Viteは
Web プロジェクトのために、より速く無駄のない開発体験を提供することを目的としたビルドツール
2つの主要部分で構成されている。
- 非常に高速なHMRなど、ネイティブESモジュールを利用した豊富な拡張機能を提供する開発サーバー
- Rollupでコードをバンドルするビルドコマンド。プロダクション用に高度に最適化された静的アセットを出力するように事前に設定されている。
1によって、変更を高速に適用する開発環境を提供することができる。
2によって、プロダクションデプロイするためのビルドができる。

似たツールのwebpackだと、基本2の機能のみ。
1の機能は大抵開発に必要なので、web-pack-dev-serverを用いたりRailsなどのバックエンドからエントリーポイントを配信する構成などがある。

ブラウザー対応
開発中、Vite は esnext を変換ターゲットとして設定します。
つまりコードは最新のJavaScriptとCSS機能が使える前提でコードをビルドするということ。
本番ビルドの場合、Vite はデフォルトで ネイティブ ES モジュール、ネイティブ ESM 動的インポート、import.meta、Null 合体演算子、BigInt などのモダンな JavaScript をサポートするブラウザーをターゲットとします。
本番は最新ではなく、一定モダンなJavaScriptをサポートするブラウザーをターゲットとしている。
レガシーブラウザに対応したい場合は @vitejs/plugin-legacy というプラグインがあるみたい。

なるほど。開発と本番でそう分けるのは理にかなっていそう。
本番ビルドの場合、ある程度幅広いブラウザで使えるものにしたいので、対応するブラウザターゲットを広げておきたい。
一方開発時は開発者が動作確認をするため、”最新のブラウザーが使用され、最新の JavaScript および CSS 機能がすべてサポートされていることを前提”としている。

最初の Vite プロジェクトを生成する
$ npm create vite@latest
で作成できる。
また、プロジェクトのテンプレートも指定可能。
$ npm create vite@latest my-vue-app -- --template vue

手動インストール
プロジェクト内にvite CLIをインストールして使うこともできる
$ npm install -D vite
<p>Hello Vite!</p>
$ npx vite
これで localhost:5173 でindex.htmlが配信される

これが 1 の開発サーバーとしてのViteだね

index.html とプロジェクトルート
お気づきかもしれませんが、Vite プロジェクトでは index.html は public 内に隠れているのではなく、最も目立つ場所にあります。これは意図的なものです。開発中、Vite はサーバーで、index.html はアプリケーションのエントリーポイントです。
確かに、publicに配置されているイメージはあるけど…🤔
Vite は index.html をソースコードとして、またモジュールグラフの一部として扱います。JavaScript のソースコードを参照している <script type="module" src="..."> を解決します。インラインの <script type="module"> や <link href> で参照される CSS も Vite 固有の機能を利用できます。
静的なhtmlファイルとしてではなく、ソースコードとして扱う、ということなのかな。そうすることで、HTML内のJavaScriptコードのモジュール解決ができる、JavaScriptをmoduleで読み込んだりCSSの参照でもVite固有の機能が使える。(固有機能ってなんだろうね)
さらに、index.html 内の URL は自動的にリベースされるため、特別な %PUBLIC_URL% プレースホルダーは必要ありません。
これどゆことやろ。なんとなく言ってることはわかるけど具体的には想像できない。

静的な http サーバーと同様に、Vite には、ファイルの提供元となる「ルートディレクトリー」の概念があります。ドキュメントの残りの部分では <root> として示されています。ソースコード内の絶対 URL は、プロジェクトルートをベースとして使って解決されるため、通常の静的ファイルサーバーを使用しているかのようにコードを記述できます(遥かに強力であることを除いては!)。
index.htmlを配置して vite コマンドでサーバー起動したディレクトリがルートとなる。
そこをルートとしたパスなので絶対パスなんだな。(ホストマシンからしたら相対パスじゃねって一瞬思った)
Vite はルート外のファイルシステムの場所に解決される依存関係を処理することもできるため、モノレポベースの構成でも使用できます。
すごそう。
Vite は複数の .html エントリーポイントを持つマルチページアプリにも対応しています。
これどういうときに使うんだろう。1つは見たことあるけど。

プロジェクト理念
無駄のない拡張可能なコア
Viteは、Webアプリケーションを構築する最も一般的なパターンをサポートすることにフォーカスしていて、あらゆるユースケースをカバーするつもりはない。ただ、プロジェクトの長期的な保守性を維持するために、Viteのコアは小さなAPIサーフェスで無駄のない状態を維持しておく必要がある。
Webアプリケーション構築のための拡張は、可能なら外部プラグインとして実装される。

モダン Web を推し進める
Vite は、モダンなコードを書くことを後押しする opinionated な機能を提供します。
モダンWebを進めることを強く推奨するほうな方針で機能提供してる。
過去の経緯とか小難しいことをどんどん意識しなくていい世界になるなら嬉しい。
以下が例。
- ソースコードは ESM でのみ書くことができます。ESM 以外の依存関係を動作させるには ESM として事前にバンドル する必要があります。
- Web ワーカーは最新の標準に従うため、new Worker 構文 で書くことが推奨されます。
- Node.js モジュールはブラウザーでは使用できません。

パフォーマンスへの実用的なアプローチ
Vite はその始まり以来、パフォーマンスに重点を置いてきました。その開発サーバーアーキテクチャーは、プロジェクトの規模が大きくなっても高速なまま維持する HMR を可能にします。
Viteはパフォーマンスに重点を置いている。
Vite は esbuild や SWC のようなネイティブツールを使って集中的なタスクを実装しますが、スピードと柔軟性のバランスを取るために残りのコードは JS に保持します。必要に応じて、フレームワークのプラグインはユーザーコードをコンパイルするために Babel を利用します。そしてビルド時に Vite は現在 Rollup を使用しています。バンドルサイズやプラグインの幅広いエコシステムにアクセスすることが単純なスピードよりも重要です。Vite は、API の安定性を保ちつつ、DX を向上させる新しいライブラリーが登場すればそれを使用し、内部的に進化し続けます。
一方で、実用性とのバランスも大切にしている。
単純なスピードよりも、幅広いエコシステムにアクセスすることを重要視しているため、エコシステムの進化を受けてViteも進化することができる。

Vite 上にフレームワークを構築する
フレームワークを作成するためのツールとして輝いています
おそらくほぼこれだよね。
Vite は SSR プリミティブのサポートを含んでいます。
Viteだけ?でSSRまで対応できるんだな
Vite はまた、Ruby や Laravel のようなバックエンドフレームワークと組み合わせるときにも最適です
ほんと実用的だ

アクティブなエコシステム
エコシステムの開発が盛り上がるようにいろいろやってるし、開発をサポートするためのツールもある。ViteのDiscordからコミュニティに参加することができる。

Vite を使う理由
ここは勉強になりそう。
問題点
ES モジュールがブラウザーで利用できるようになるまで、開発者はモジュール化された JavaScript を生成するネイティブの仕組みを持っていませんでした。これは、私たちが「バンドル」のコンセプトに慣れ親しんでいる理由でもあります: すなわち、ブラウザーで実行可能なようにソースモジュールをクロール、処理し、連結するツールを使用しています。
フロントエンドビルドやWebpackについて学んだときに、Webpackがないとどう辛かったか、の話だよな。モジュールシステムがないと一番最悪なケースで1つのjsファイルにひたすら処理を足していくことになる。
だから、開発するときはソースをモジュールで分割しておいて、最終的にプロダクション用にツールでバンドルしてブラウザで実行できるようにしていた。

しかしながら、大規模なアプリケーションが作られるようになってくると、取り扱う JavaScript の量は劇的に増加しました
JavaScript ベースのツールを使用していては、いずれパフォーマンスのボトルネックにぶつかります: 開発サーバーを起動するのにやたらと長く待つこともあります(数分かかることさえ!)。また、Hot Module Replacement(HMR)を利用していても、ファイル編集がブラウザーに反映されるまで数秒かかることもあります。フィードバックの遅さが継続することは、開発者の生産性や幸福度に大きな影響を与える可能性があります。
理解

Vite では新しいエコシステムの進歩を活用し、これらの問題を解決することに取り組んでいます: ブラウザーのネイティブ ES モジュールや、ネイティブにコンパイルされる言語で書かれた先進的な JavaScript ツールの利用です。
ESモジュールがブラウザで利用できるようになった世界で、それらを前提としたツールを利用して、大規模アプリケーションの開発サーバー起動・HRMの遅さによる課題を解決するのがVite。

遅いサーバー起動
開発サーバーがコールドスタートするとき、バンドラーベースのビルドセットアップは、アプリケーション全体を提供する前に、アプリケーション全体を隅々までクロールしてビルドする必要があります。
Vite はまず最初にアプリケーションのモジュールを 2 つのカテゴリーに分割することで、開発サーバーの起動時間を改善します: 依存関係とソースコードです。
これまでのツールが、アプリケーション内のすべての同一にビルドして遅かったのに対して、Viteはコードを2つに分類して、それぞれに適切な処理をすることで高速化している
依存関係
依存関係に分類されるのは、”開発中にあまり変更されないプレーンなJavaScript”
これをesbuildを仕様して、事前バンドル(?)を行う。esbuildはGo言語で開発されており、JSベースよりも10‐100倍高速。
ソースコード
ソースコードに分類されるコードは、変換を必要とするプレーンJSではないものがよく含まれており、よく編集されるという違いがある。(JSX,CSS,Vue/Svelteコンポーネントなど)。ルーティングによるコード分割があるためすべてのソースコードを同時に読み込む必要がないというのも違い。
ソースコードに対してViteはネイティブESMを行使してソースコードを提供する。
ブラウザーは、実質的にバンドラーの仕事の一部を引き受けます: Vite はブラウザーのリクエストに応じて、ソースコードを変換し提供するのみになります。
サーバー起動時、すべてのコードが完全にバンドルされるまで待つ必要があったときに比べて、ソースコードはビルドしないので起動が早い。Viteでは、ソースコードはリクエストに応じて変換して提供するのみになり、ネイティブESMを行使することでブラウザがこれまでのバンドラーのしごとの一部を引き受けることになっている。

遅い更新速度
バンドラーベースのビルドでファイル編集時に全体をビルドし直すのは明らかに非効率。
また、一部のみ再処理するとしても、バンドル全体の再構築はコストがかかり、ページリロードによって状態が消えてしまう。
そこでホットモジュールリプレースメントがある。が、これもアプリケーションが大きくなるにつれて更新速度が著しく悪化するみたい。
Vite では、HMR をネイティブ ESM 上で行います。
ネイティブESMによるHMRでアプリケーションサイズに関係なく高速化される。
また、Vite は HTTP ヘッダーを活用して、フルページのリロードも高速化します(ここでも、ブラウザーにはもっと働いてもらいます): ソースコードモジュールのリクエストでは 304 Not Modified を利用して条件が作成されます。そして、依存モジュールのリクエストでは、一度キャッシュされたものが再びサーバーにヒットしないよう、Cache-Control: max-age=31536000,immutable を利用して積極的にキャッシュされます。

変更されたモジュール単位で再評価すればHMRが実現できる。
webpackでもチャンク化やHMRをめっちゃ頑張って実現しているが、頑張っている分少し遅い。

プロダクションではバンドルする理由
ネイティブESMによってバンドル・ビルドしなくても動かせるようになったとしても、プロダクションでそれを動かすのは非効率。
これは、ネットワークのラウンドトリップの増加がネストされたインポートによって引き起こされるためです
プロダクションでは最適化されたローディングパフォーマンスを得るために、ツリーシェイキングや遅延読み込み、(キャッシュ改善のための)共通コード分割などの技術を用いつつバンドルを行うことは、より良いことです。
開発サーバーほどの更新速度は必要なく、よりパフォーマンスが求められるので少し時間がかかってもバンドルしたものを使うってことだな

なぜ esbuild でバンドルしないのか?
esbuildの速さよりもRollupの柔軟なプラグインAPIを優先。
Rollupの方がパフォーマンスと柔軟性のトレードオフに優れていると考えられている。
また、RolldownというRollupをRustで再構築する取り組みが進行しており、それが完了するとesbuildとRollupの両方が置き換えられるため、ビルドパフォーマンスの向上はもちろん、開発とビルドの不一致が解消される。
→つまり今esbuildで開発ビルドとRollupで本番ビルドという形なので、多少ビルドの不一致が起きることがあるんだね。

Vite は他の非バンドルのビルドツールとどう違いますか?
他の非バンドルのビルドツールが3つあげられている。
- WMR
- Snowpack
- @web/dev-server(旧称es-dev-server)
どのツールからもViteは影響を受けて開発されたようです。
うちWMRとSnowpackは現在メンテナンスされていないため、代替ツールとしては挙がらないと思われます。@web/dev-server は今も活発にメンテナンスされているようですが、その違いについてはあまりわからない。

特徴
基本的に、Vite を使用した開発は静的ファイルサーバーを使用した時とそれほど変わりません。しかし、Vite はバンドラーベースのセットアップで一般的な機能をサポートするためにネイティブ ESM をインポートすることで様々な拡張機能を提供します。
ベースは静的ファイルサーバー。一般的なバンドラーの機能を使うためにネイティブESMのインポートで拡張することができる。

npm の依存関係の解決と事前バンドル
ネイティブ ES のインポートは次のような生のモジュールをサポートしていません:
import { someMethod } from 'my-dep'
ネイティブESのインポートだけだと npmパッケージをimportする↑の書き方は使えないってことか。
つかうなら node_modules の中のファイルパスを指定しないといけないって話かな
ite は提供される全てのソースファイルでこのような生のモジュールのインポートを検出し以下を実行します:
事前バンドル はページの読み込み速度を改善し、CommonJS / UMD モジュールを ESM に変換します。事前バンドルは esbuild で実行され、Vite のコールドスタート時間をどんな JavaScript ベースのバンドラーよりも大幅に高速にします。
モジュールのimportに対しては事前バンドルが適用される。(参照)
事前バンドルをしておく理由
インポートを /node_modules/.vite/deps/my-dep.js?v=f3sf2ebd のように書き換えることでブラウザーが正しくモジュールをインポートできるようにします。
これでESMでインポートできる形に変換されると。
依存関係は積極的にキャッシュされます
Vite は HTTP ヘッダーを介して依存関係のリクエストをキャッシュするため、依存関係をローカルで編集/デバッグする場合は、ここの手順に従ってください。
この辺はあまりイメージわかない。

ネイティブESM = HTTPの仕組みを使ってモジュールを取得するということ。
Viteはnode_moduls配下のモジュールはHTTPの仕組みでキャッシュしている。

Hot Module Replacement
ViteはVueのSFCやReact fast Refresh に対してのHMR統合を提供する。
これは、Vueだったら@vitejs/plugin-vueで使えるようになっている。
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
})

TypeScript
Viteは.tsファイルのインポートをサポートしているが、トランスパイルを行うだけで型チェックは行わない。
Vite が変換処理の一部として型チェックを行わないのは、この 2 つのジョブが根本的に異なる動作をするからです。トランスパイルはファイル単位で行うことができ、Vite のオンデマンドコンパイルモデルと完全に調和しています。これに対して、型チェックはモジュールグラフ全体についての知識が必要です。Vite の変換パイプラインに型チェックを組み込むと、必然的に Vite の利点であるスピードが損なわれてしまいます。
理由なるほど。

Viteに限らず、トランスパイルと型チェックを分けるのは一般的になっている。
Babelでトランスパイルして、CIでtscを実行する、など。

TypeScript コンパイラーオプション
isolatedModules
: trueにする必要がある。
TBD