2021/02 に Vite 2.0 正式版がリリースされ、開発サーバーの起動や HMR (Hot Module Replacement) の動作が軽快なフロントエンド開発環境を手軽に構築できるようになってきました。
これとほぼ同時期に、既存の Vue 2.x + Vue CLI 環境に Vite 環境を同居させ、開発ビルドにだけ Vite を活用する vue-cli-plugin-vite
がリリースされました。このプラグインは「コードベースの変更なしに」を謳っていて、Vue CLI 向けの設定ファイル vue.config.js
を Vite 向けの設定に変換1することで Vite 開発サーバーを起動させる機能を持っています。
実際に Vue CLI 環境へ導入してみたところ、「コードベースの変更なしに」は概ね正しかったものの vue.config.js
などに修正が必要でした。
設定に若干難しさはあったものの、開発サーバーの起動時間が 3 秒程度になって HMR の動作も大幅に改善されました。
本記事では、設定ファイルの修正方法や Vite 開発サーバーにおける注意点などを共有します。
動作確認環境
- macOS Catalina
- Chrome 89
- Node.js 14
- npm 6
- Vue CLI v4.5.11
-
vue
2.6.12 -
vue-cli-plugin-vite
0.3.2 -
typescript
3.9.9 -
sass
(dart-sass) 1.32.8 -
sass-loader
8.0.2
-
プラグインの導入
Vue 2.x + Vue CLI 環境のディレクトリ上で vue add vite
または npx @vue/cli add vite
を実行してプラグインを導入します。
プラグインを導入すると、package.json
に npm run vite
のタスクが追加され、 bin/vite
というシェバン付きの JavaScript ファイルが作成されます。
{
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"vite": "node ./bin/vite"
}
}
各種設定
開発サーバー立ち上げ時にブラウザを起動しないようにする設定
環境変数 BROWSER=none
をセットします:
{
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"vite": "BROWSER=none node ./bin/vite"
}
}
テンプレート上での空白の取り扱いの設定
Vue CLI 環境では whitespace
のデフォルト設定が condense
でしたが、Vite 環境では preserve
になっていました。
condense でのレンダリング結果 |
preserve でのレンダリング結果 |
この違いによって、ブラウザ上での余白の表示が変化することもあるため、Vite 開発環境においても whitespace
を condense
にする設定を追加します:
module.exports = {
pluginOptions: {
vite: {
vitePluginVue2Options: {
vueTemplateOptions: {
compilerOptions: {
whitespace: 'condense',
},
},
},
},
},
};
ネストが深くなっていてわかりにくいですが、各種オプション名は以下ような対応となっています:
-
vite
:vue-cli-plugin-vite
の設定 -
vitePluginVue2Options
:vite-plugin-vue2
の設定 -
vueTemplateOptions
:@vue/component-compiler-utils
の設定 -
compilerOptions
:vue-template-compiler
の設定
<style lang="scss">
ブロックへのコードの自動挿入設定
Vue CLI 環境には .vue
ファイルの <style lang="scss">
ブロック2に変数等を自動挿入する設定があります。
これを Vite 環境でも利用するには、vue.config.js
を以下のように設定します:
-
css.loaderOptions.sass
で設定している場合はcss.loaderOptions.scss
に変更する -
sass-loader
v8 の場合、Vue CLI 環境ではcss.loaderOptions.scss.prependData
として振る舞い、 Vite 環境ではcss.loaderOptions.scss.additionalData
として振る舞うように設定する3
// Vite 開発環境であるかを判定するための変数を用意する。
// ここでは「開発サーバー立ち上げ時にブラウザを起動しないようにする」の設定した上で、その設定の有無にて判定します。
const IS_VITE_ENV = 'BROWSER' in process.env;
module.exports = {
css: {
loaderOptions: {
scss: {
[IS_VITE_ENV ? 'additionalData' : 'prependData']: `
$main-color: #123456;
`,
},
},
},
};
バンドル結果を別のサーバーで配信している環境の場合
Vue CLI 環境の開発ビルド(npm run serve
)でディスク書き込みを有効にして、バンドル結果の js ファイルや css ファイルを別のサーバー環境で配信している場合もあるかと思います。
この場合は Vite 開発サーバー起動と同時に、Vite 開発サーバーが配信するモジュールを読み込むためのコードをディスクに書き込む必要があります。
ここでは以下の状況を仮定します:
-
src/main.ts
がエントリーポイント- 出力先は
dist/app.js
- 出力先は
-
npm run serve
時のディスク書き込みが有効 - Vue CLI / Vite 開発サーバーのポート番号は
33333
-
http://localhost:8080/foo/bar/
でdist/
の内容を配信している -
localhost:8080
側で配信する HTML に<script src="/foo/bar/app.js"></script>
が埋め込まれている
module.exports = {
configureWebpack: {
output: {
filename: '[name].js',
},
},
devServer: {
port: 33333,
writeToDisk: true,
},
publicPath: `/foo/bar/`,
};
vue-cli-plugin-vite
は devServer.writeToDisk
までは考慮しないので、dist/app.js
に Vite 開発サーバー側のモジュールを参照させるコードを書き込む必要があります。
dist/app.js
の書き込み
http://localhost:33333/foo/bar/src/main
を読み込むように設定します。
この dist/app.js
はモジュールではなく通常のスクリプトであるため、import
文ではなく動的 import()
を利用します4:
import('http://localhost:33333/foo/bar/src/main');
npm run vite
実行時に上記内容を書き込むため、bin/vite
に以下の内容を追記します:
// 以下をファイル下部に追記する
const fs = require('fs').promises;
// const path = require('path'); は既に定義されている
const getPath = pathSegment => path.resolve(__dirname, '../', pathSegment);
(async () => {
// dist ディレクトリがない場合は作成する
await fs.mkdir(getPath('dist/')).catch(() => {});
// dist/app.js を書き込む
await fs
.writeFile(getPath('dist/app.js'), `import('http://localhost:33333/foo/bar/src/main');`)
.catch(e => console.error(e));
})();
画像ファイルの配信するためにシンボリックリンクを作成
.vue
ファイルのテンプレートに <img src="@/assets/logo.png">
のように書くと <img src="/foo/bar/src/assets/logo.png">
のような img タグがレンダリングされるため、http://localhost:33333/foo/bar/src/assets/logo.png
ではなく http://localhost:8080/foo/bar/src/assets/logo.png
を参照しようとしてしまいます。
記事投稿時点では適切な設定オプションが無かった5ため、やむを得ず dist/
に src/
へのシンボリックリンクを作成して localhost:8080
側で画像ファイルを配信します:
$ cd dist/
$ ln -s ../src/ src
npm run vite
実行時に上記の操作をするため、 以下のコードを bin/vite
の await fs.mkdir(...);
の下に追記します:
await fs.symlink('../src/', getPath('dist/src')).catch(() => {});
/
から始まるパスの画像ファイルを読み込めるようにする
.vue
ファイルのテンプレートに <img src="/img/photo.jpg">
のように書いて http://localhost:8080/img/photo.jpg
を参照しようとすると、Vue CLI 環境では問題なくコンパイルできるものの Vite 開発環境では以下のようなコンパイルエラーが出てしまいます:
[vite] Internal server error: Failed to resolve import "/img/photo.jpg". Does the file exist?
vite-plugin-vue2
の実装を追っていったところ、以下のようにして transformAssetUrlsOptions.forceRequire
の設定を追加する必要がありました:
module.exports = {
pluginOptions: {
vite: {
vitePluginVue2Options: {
vueTemplateOptions: {
compilerOptions: {
whitespace: 'condense',
},
transformAssetUrlsOptions: {
forceRequire: false,
},
},
},
},
},
};
注意事項
ES モジュールを用いていること
Vite 開発サーバーはモジュールの利用を前提としています。
特に IE11 はモジュールに非対応のため、IE11 で動作確認するには従来通り Vue CLI 環境を用いてバンドルする必要があります。
型エラーやフォーマットエラーはターミナル上のログとして表示されない
Vite は TypeScript による型チェックや Linter によるフォーマットチェックを行いません。エディタでの保存時に自動でフォーマットをする設定や、CI でエラーを検知できる仕組みなどを導入しておく必要があるでしょう。
esbuild を利用している
esbuild をトランスパイラとして用いているため、OS と CPU アーキテクチャに依存するバイナリが node_modules
にインストールされます。
特に Docker や仮想マシンを利用している場合は、異なる OS 間で node_modules
ディレクトリを共用できないことに注意が必要です。
$ file node_modules/esbuild/bin/esbuild
node_modules/esbuild/bin/esbuild: Mach-O 64-bit executable x86_64
まとめ
Vue CLI 環境ではプロジェクトの規模が大きくなればなるほど開発ビルドが重くなり、開発体験が悪くなっていく傾向がありましたが、vue-cli-plugin-vite
のおかげでこの問題を解決できました。
設定に若干難しさがありますが、本記事を参考に Vite 開発環境を試していただければ幸いです。
-
scss + Scoped CSS (
<style lang="scss" scoped>
) や scss + CSS Modules (<style lang="scss" module>
) も対象 ↩ -
sass-loader
v7 の場合はcss.loaderOptions.scss.data
, v9 の場合はcss.loaderOptions.scss.additionalData
を用います。v9 以降であれば三項演算子と Computed property names による分岐は不要だと思われます。Vue CLI で環境構築した場合のデフォルトは v8 になっています(記事投稿時点) ↩ -
参照: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/import ↩