63
55

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Vue 2.x】Vue CLI環境にViteを導入して開発ビルドを爆速にする

Last updated at Posted at 2021-03-07

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 開発サーバーにおける注意点などを共有します。

image.png

動作確認環境

  • 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.jsonnpm run vite のタスクが追加され、 bin/vite というシェバン付きの JavaScript ファイルが作成されます。

package.json
{
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "vite": "node ./bin/vite"
  }
}

各種設定

開発サーバー立ち上げ時にブラウザを起動しないようにする設定

環境変数 BROWSER=none をセットします:

package.json
{
  "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
condense でのレンダリング結果 preserve でのレンダリング結果

この違いによって、ブラウザ上での余白の表示が変化することもあるため、Vite 開発環境においても whitespacecondense にする設定を追加します:

vue.config.js
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
vue.config.js
// 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> が埋め込まれている
vue.config.js
module.exports = {
  configureWebpack: {
    output: {
      filename: '[name].js',
    },
  },
  devServer: {
    port: 33333,
    writeToDisk: true,
  },
  publicPath: `/foo/bar/`,
};

vue-cli-plugin-vitedevServer.writeToDisk までは考慮しないので、dist/app.js に Vite 開発サーバー側のモジュールを参照させるコードを書き込む必要があります。

dist/app.js の書き込み

http://localhost:33333/foo/bar/src/main を読み込むように設定します。

この dist/app.jsモジュールではなく通常のスクリプトであるため、import 文ではなく動的 import() を利用します4:

dist/app.js
import('http://localhost:33333/foo/bar/src/main');

npm run vite 実行時に上記内容を書き込むため、bin/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/viteawait 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 の設定を追加する必要がありました:

vue.config.js
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 開発環境を試していただければ幸いです。

  1. どのオプションが変換の対象となるかについては README.md を参照

  2. scss + Scoped CSS (<style lang="scss" scoped>) や scss + CSS Modules (<style lang="scss" module>) も対象

  3. sass-loader v7 の場合は css.loaderOptions.scss.data, v9 の場合は css.loaderOptions.scss.additionalData を用います。v9 以降であれば三項演算子と Computed property names による分岐は不要だと思われます。Vue CLI で環境構築した場合のデフォルトは v8 になっています(記事投稿時点)

  4. 参照: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/import

  5. 参照: https://github.com/vitejs/vite/pull/2254

63
55
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
63
55

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?