Viteは開発者の間でますます人気が高まっていますが、コミュニティは ( Webpackほど) 大きくないため、問題を解決するには独自のカスタム プラグインを作成する必要がある場合があります。この記事では、 Vite用のプラグインを作成する方法について説明し、私自身のプラグインについて詳しく説明します。
プラグインを作成するには、Vite が開発サーバー (コマンドvite
) とバンドル (コマンドvite build
) に異なるビルド システムを使用することを理解しておくことが重要です。
開発サーバーの場合、最新のブラウザでサポートされているネイティブ ES モジュールを備えたesbuildを使用します。コードを 1 つのファイルにバンドルする必要がなく、高速な HRM (ホット モジュール交換) が可能です。
バンドルには、柔軟で大規模なエコシステムがあるため、 rollup.jsが使用されます。これにより、さまざまな出力形式で高度に最適化された本番バンドルを作成できます。
Vite のプラグイン インターフェイスは Rollup に基づいていますが、開発サーバーと連携するための追加のオプションとフックが付いています。
プラグインを作成するときは、それをvite.config.js
にインライン化できます。新しいパッケージを作成する必要はありません。プラグインがプロジェクトで役立つことがわかったら、それをコミュニティと共有し、Vite エコシステムに貢献することを検討してください。
また、rollup.js にはより大きなコミュニティとエコシステムがあるため、rollup.js 用のプラグインを作成することを検討すると、Vite でも同様に機能します。したがって、プラグイン機能がバンドルに対してのみ機能する場合は、Vite の代わりに rollup.js プラグインを使用でき、ユーザーは Vite プロジェクトでロールアップ プラグインを問題なく使用できます。
rollup 用のプラグインを作成すると、rollup.js のみを使用するより多くのユーザーをカバーできます。プラグインが開発サーバーに影響を与える場合は、Vite プラグインを使用します。
vite.config.ts
で直接プラグインの作成を開始しましょう。
// vite.config.ts import { defineConfig, Plugin } from 'vite'; function myPlugin(): Plugin { return { name: 'my-plugin', configResolved(config) { console.log(config); }, }; } export default defineConfig({ plugins: [ myPlugin(), ], });
この例では、開発サーバーとバンドルの両方の段階でコンソールで Vite 設定が解決されるとすぐに Vite 設定を出力するmyPlugin
というプラグインを作成しました。 dev サーバー モードでのみ構成を出力したい場合は、バンドルにapply: 'serve'
とapply: 'build'
を追加する必要があります。
// vite.config.ts import { defineConfig, Plugin } from 'vite'; function myPlugin(): Plugin { return { name: 'my-plugin', apply: 'serve', configResolved(config) { console.log(config); }, }; } export default defineConfig({ plugins: [ myPlugin(), ], });
また、プラグインの配列を返すこともできます。これは、開発サーバーとバンドルの機能を分離するのに役立ちます。
// vite.config.ts import { defineConfig, Plugin } from 'vite'; function myPlugin(): Plugin[] { return [ { name: 'my-plugin:serve', apply: 'serve', configResolved(config) { console.log('dev server:', config); }, }, { name: 'my-plugin:build', apply: 'build', configResolved(config) { console.log('bundle:', config); }, }, ]; } export default defineConfig({ plugins: [ myPlugin(), ], });
ほぼそれだけです。 Vite 設定に小さなプラグインを簡単に追加できます。プラグインが大きくなりすぎる場合は、別のファイルに移動するか、パッケージを作成することを好みます。
もっと複雑なものが必要な場合は、Vite のドキュメントで調べることができる便利なフックがたくさんあります。ただし、例として、以下に私自身のプラグインを分解してみましょう。
そこで、アイコン ファイルに基づいて SVG スプライトを作成するプラグインvite-plugin-svg-spritemapを用意しました。
目標は、 src/icons
フォルダー内のすべてのアイコン.svg
取得し、その内容を SVG スプライトと呼ばれる単一の.svg
ファイルに収集することです。バンドル段階から始めましょう。
import { Plugin, ResolvedConfig } from 'vite'; import path from 'path'; import fs from 'fs-extra'; function myPlugin(): Plugin { let config: ResolvedConfig; return { name: 'my-plugin:build', apply: 'build', async configResolved(_config) { config = _config; }, writeBundle() { const sprite = getSpriteContent({ pattern: 'src/icons/*.svg' }); const filePath = path.resolve(config.root, config.build.outDir, 'sprite.svg'); fs.ensureFileSync(filePath); fs.writeFileSync(filePath, sprite); }, }; }
フックconfigResolved
使用すると、次のフックで使用することが解決されたときに構成を取得できます。
バンドル プロセスの完了後にwriteBundle
フックが呼び出されます。ここで、 sprite.svg
ファイルを作成します。
getSpriteContent
関数は、 src/icons/*.svg
パターンに基づいて、準備された SVG スプライトの文字列を返します。これについてはこれ以上深くは説明しません。 SVG スプライト生成のプロセス全体を説明した私の他の記事をチェックしてください。
次に、 path.resolve()
で SVG スプライト コンテンツを配置するsprite.svg
への絶対パスを作成し、 fs.ensureFileSync
でファイルが存在することを確認 (または作成) し、そこに SVG スプライト コンテンツを書き込みます。 。
ここで、最も興味深い部分である開発サーバーの段階について説明します。ここではwriteBundle
使用できず、開発サーバーの実行中はファイルをホストできないため、サーバーミドルウェアを使用してsprite.svg
へのリクエストをキャッチする必要があります。
import { Plugin, ResolvedConfig } from 'vite'; function myPlugin(): Plugin { let config: ResolvedConfig; return { name: `my-plugin:serve`, apply: 'serve', async configResolved(_config) { config = _config; }, configureServer(server) { // (1) return () => { server.middlewares.use(async (req, res, next) => { // (2) if (req.url !== '/sprite.svg') { return next(); // (3) } const sprite = getSpriteContent({ pattern, prefix, svgo, currentColor }); res.writeHead(200, { // (4) 'Content-Type': 'image/svg+xml, charset=utf-8', 'Cache-Control': 'no-cache', }); res.end(sprite); }); }; }, }; }
configureServer
は開発サーバーを構成するためのフックです。 Vite の内部ミドルウェアがインストールされる前にトリガーされます。私の場合、内部ミドルウェアの後にカスタムミドルウェアを追加する必要があるため、関数を返します。
開発サーバーへのすべてのリクエストをキャッチするカスタム ミドルウェアを追加するには、 server.middlewares.use()
を使用します。ファイルの動作をエミュレートできるように、URL [localhost:3000/sprite.svg](http://localhost:3000/sprite.svg)
リクエストを検出するために必要です。
リクエスト URL が/sprite.svg
ではない場合 - 次のミドルウェアにスキップします (つまり、チェーン内の次のハンドラーに制御を渡します)。
ファイルのコンテンツを準備するには、 getSpriteContent
の結果を変数sprite
に入れ、設定されたヘッダー (コンテンツ タイプと 200 HTTP ステータス) を含む応答として送信します。
その結果、ファイルの動作をシミュレートしました。
ただし、 src/icons
内のファイルが変更、削除、または追加された場合は、サーバーを再起動して、 getSpriteContent
を介して新しいスプライト コンテンツを生成する必要があります。このために、ファイル監視ライブラリ - chokidarを使用します。 chokidar ハンドラーをコードに追加しましょう。
import { Plugin, ResolvedConfig } from 'vite'; import chokidar from 'chokidar'; function myPlugin(): Plugin { let config: ResolvedConfig; let watcher: chokidar.FSWatcher; // Defined variable for chokidar instance. return { name: `my-plugin:serve`, apply: 'serve', async configResolved(_config) { config = _config; }, configureServer(server) { function reloadPage() { // Function that sends a signal to reload the server. server.ws.send({ type: 'full-reload', path: '*' }); } watcher = chokidar .watch('src/icons/*.svg', { // Watch src/icons/*.svg cwd: config.root, // Define project root path ignoreInitial: true, // Don't trigger chokidar on instantiation. }) .on('add', reloadPage) // Add listeners to add, modify, delete. .on('change', reloadPage) .on('unlink', reloadPage); return () => { server.middlewares.use(async (req, res, next) => { if (req.url !== '/sprite.svg') { return next(); } const sprite = getSpriteContent({ pattern, prefix, svgo, currentColor }); res.writeHead(200, { 'Content-Type': 'image/svg+xml, charset=utf-8', 'Cache-Control': 'no-cache', }); res.end(sprite); }); }; }, }; }
ご覧のとおり、プラグイン作成の API はそれほど複雑ではありません。 Vite または Rollup から自分のタスクに合ったフックを見つけるだけです。この例では、Rollup.js のwriteBundle
(前述したように、バンドルの生成に使用されます) を使用し、Rollup.js にはネイティブの開発サーバー サポートがないため、Vite のconfigureServer
使用しています。
writeBundle
の場合は非常に単純で、SVG スプライトのコンテンツを取得してファイルに入れました。開発サーバーの場合、なぜ同じことができないのか混乱しました。他の作者のプラグインも調べてみましたが、どれもほぼ同じことを行います。
そこで、 configureServer
を使用し、 server
引数を介して、 sprite.svg
リクエストをインターセプトすることで開発サーバーへのすべてのリクエストをトリガーするミドルウェアを追加します。
前に述べたように、より便利なプラグインを作成するには、フックを調べる必要があります。それらについてはドキュメントで詳しく説明されています。
https://vitejs.dev/guide/api-plugin#universal-hooks
https://vitejs.dev/guide/api-plugin#vite-specific-hooks
命名に関しては、Vite にはプラグインに関するいくつかの規則があるため、終了する前に確認しておくことをお勧めします。いくつかの重要なポイントを次に示します。
vite-plugin-
プレフィックスが付いた一意の名前が必要です。
vite-plugin
キーワードを package.json に含めます。
vite-plugin-vue-
、 vite-plugin-react-
、 vite-plugin-svelte-
)。プラグインを NPM で公開することに決めた場合は、知識と専門知識を共有することが IT コミュニティの基本原則であり、集団的な成長を促進するため、これをお勧めします。パッケージを公開および保守する方法については、私のガイドを参照してください → NPM パッケージを作成する最も簡単な方法。
また、プラグインを vite のコミュニティ リスト ( awesome-vite)に送信することを強くお勧めします。そこでは多くの人が最適なプラグインを探しており、Vite エコシステムに貢献する絶好の機会となるでしょう。プラグインを送信するプロセスは簡単です。条件を満たしていることを確認してプル リクエストを作成するだけです。用語のリストはここでご覧いただけます。
全体として、(ロールアップではなく) Vite に特化し、オープンソースであり、適切なドキュメントが必要です。プラグインを頑張ってください!