🦦

Vue 3 / Vuetify 3 に備えてまず Vite への移行をお勧めしたい

2022/07/29に公開

ロンラン株式会社 CEO 兼 CTO の武部です。

もうすぐ Vuetify 3 の beta が取れて GA 版が届きそうですね!

当社ロンランの主要 Web 製品では Vuetify 2 を採用しています。このため、必然 Vue 2 を使い続けざるを得なかったのですが、エコシステムがどんどん進化してゆくなか、いつまでも Vue 3 へ移行できないもどかしさがありました。

まずは来たるべき Vuetify 3 へ向けて、試しに一部機能を Vue 3 / Vuetify 3 beta / Vite へ移行してみることにしました。しかし、対処すべきことがなかなか多く、簡単な一部機能を移行し終えただけで燃え尽き気味。これはなかなか苦労することになるぞ...😫と渋い気持ちになりました。

ある日、Vite は Vue 2 にも対応していると気づき、対応ステップを分けて移行することに思い至りました

計画としては次のとおりです。

  • 現状: Vue 2, Vuetify 2, vue-cli

↓↓↓

  • Step 1: Vue 2, Vuetify 2 はそのままに、Vue CLI を Vite へ
  • Step 2: Vuetify 3 が GA になったら、Vue3, Vuetify 3 へ

当社の主力製品はふたつあり、うちのやや複雑な製品の Step 1 対応には、試行錯誤しながら丸一日程度の時間を費やしました。
そのまま続けて 2 つ目の製品に着手した際は、1.5 時間で移行できました(記憶が残っているうちに対応したい!という思いで、立て続けに対応しました)。

vite 移行後の分かりやすいメリット

Vite 移行対応の所感ですが、Step を分けたことで全体のマイグレーション総コストを下げることができたと感じています。感覚的には、Vue 3 と Vuetify 3 への移行よりも、Vite への移行の方が、対処範囲が広い印象を持ちました。すでにそれを終えたので、今後の Vue3, Vuetify 3 への移行は、いまはただ待ち遠しいです🥳

また、Vite 化だけでも開発者体験の向上、生産性向上の恩恵は大きいです。たとえば以下です。

  • 開発サーバ起動やビルドが圧倒的に速い
  • workspace 内の共用コードを package として提供しているが、この build をシンプルにできた
  • build したリソースの動作確認が簡単にできるようになった

開発サーバ起動とビルドについては、起動時間を比較してみました。

vs 開発サーバ起動 ビルド
vue-cli 45s 50s
vite 518ms
(90 倍!)
18.5s
(2.7 倍!)

ひとつ無駄なストレスを減らすことができました。

ということでこの記事は 「同じような境遇なら、まずは Vite 化に着手してみませんか!」 というご提案です。

では実際に Vue 2 + Vuetify 2 + Vite 環境へと移行してゆきましょう。

Vite 対応手順

vite.config.js の作成

いきなり本丸で、ここが肝です。Vue 2 - Vuetify 2 - Vite の実現方法を調べていくつか試したのですが、下記がベスト解だと思います。

  • vite-plugin-vue2
  • unplugin-vue-components/resolversVuetifyResolver
  • unplugin-vue-components/viteComponents

これらがうまくブリッジしてくれるようです。

vite 自体も含めて、次のようにインストールします。

yarn add -D vite vite-plugin-vue2 unplugin-vue-components

実際の vite.config.js ファイルのコードは次のようになりました。
Vue 2.7 Naruto よりも新しいバージョンの Vue を使っている場合は @vitejs/plugin-vue2 が利用できます。
alias の設定や scss の additionalData はご自身の環境にあわせて設定してください。

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue2'
import { VuetifyResolver } from 'unplugin-vue-components/resolvers'
import Components from 'unplugin-vue-components/vite'

export default defineConfig({
  plugins: [
    vue(),
    Components({
      resolvers: [VuetifyResolver()],
    }),
  ],
  define: { 'process.env': {} },
  resolve: {
    alias: [{ find: '@', replacement: '/src' }],
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: "\n@import 'src/styles/variables.scss';\n",
      },
    },
  },
})

もし、Vue 2.7 よりも古いバージョンを使っている場合は @vitejs/plugin-vue2 ではなく vite-plugin-vue2 を利用します。

import { defineConfig } from 'vite'
import { createVuePlugin } from 'vite-plugin-vue2'
import { VuetifyResolver } from 'unplugin-vue-components/resolvers'
import Components from 'unplugin-vue-components/vite'

export default defineConfig({
  plugins: [
    createVuePlugin(),
    Components({
      resolvers: [VuetifyResolver()],
    }),
  ],
  ...

package.json の scripts 定義修正

起動方法を変えます。

vue-cli では vue-cli-service を介して開発サーバ起動とビルドを実行していましたが、

  "serve": "vue-cli-service serve",
  "build": "vue-cli-service build",

今後は vite を介して実行します。

  "dev": "vite",
  "build": "vite build",
  "preview": "vite previrew",

Vite の preveiw 機能は個人的に嬉しい点です。

開発サーバで生成されているコードと build 後のコードは、自動生成 chunk ファイルの形式が異なるなどの違いのため、たまに手元でデバッグしたい時があります。

モノレポ化している当社製品では、パスの問題があって VSCode の Live Server でもうまくプレビューできず諦めていたのですが、Vite への移行後からは簡単にデバッグできるようになりました。

public/index.html の場所変更と修正

public/index.html は、そのパッケージ内のルートディレクトリ直下に移動させる必要があります。

次に、コードを修正します。

<%= BASE_URL %> 構文は廃止です。

修正前

       <link
            rel="apple-touch-icon"
            sizes="180x180"
            href="<%= BASE_URL %>apple-touch-icon.png"
        />

修正後

       <link
            rel="apple-touch-icon"
            sizes="180x180"
            href="/apple-touch-icon.png"
        />

そして、/src/main.js<script> タグで直接読み込むようにします。これは </head> 直前に記載します。

  <script type="module" src="/src/main.js" defer></script>
</head>

なお <div id="app"></div> は index.html 内にそのまま残しておきましょう(もしなければ記載しましょう)。

環境変数周りの変更

Vue アプリケーションで環境変数を使う際は、環境変数のプレフィックスを VUE_APP_* にしておく必要がありましたが、
これを VITE_* に変更する必要があります。

ずばっと一括置換で対応するのがよいでしょう。

環境変数の利用箇所では、process.env から環境変数を取得していましたが、vite では import.meta.env から取得します。

これもずばばっと一括置換で。

なお「VITE_* なんていやだ!」とか「process.env そのま使いたいんですけど!」という場合には、それもできそうです。

ただ、あまり Vite のスタンダードから逸脱しない方がよいと思いますし、Evan You のような天才が考えている設計思想は我々常人の考えの及ぶところではないので、『郷には入っては郷に従え』というのも大事かなと私は思います。

import 時の .vue 拡張子の補完

import 文では、.vue 拡張子を省略できなくなります。これまでは何気なく拡張子を記述してこなかったのですが、ここにきて手痛い修正。

router 内での component 定義と .vue ファイル内でのコンポーネントの import 文を修正します。

上手な正規表現なら一括置換ができそうですが、自分は手動で対処してゆきました。

以下修正例です。

router

修正前

      component: () => import('@/views/Index'),

修正後

      component: () => import('@/views/Index.vue'),

.vue ファイル内

修正前

import MyComponent from '@/components/MyComponent'

修正後

import MyComponent from '@/components/MyComponent.vue'

画像のパス

これまで src/assets/images/ などの下に画像を置いて直接利用していたとしたら、

例)

    <v-img src="@/assets/images/mocks/sample-cover-art.png" />

次のいずれかの方法で修正します。

A. import する

script ブロック内に import 文で読み込み、これを利用します。

<script> ブロック内

import cover_art_img from '@/assets/images/mocks/sample-cover-art.png'

<template> ブロック内

    <v-img :src="cover_art_img" />

これは「ビルドに含める必要がある、コンポーネントの一部としての画像」を対象にすべきだと思います。大抵、あまり多くない印象です。

B. public/ ディレクトリ配下に配置する

画像を public/ 配下にファイル移動して、それを参照します。

たとえば src/assets/images/sample-cover-art.pngpublic/images/ へ移動しつつ、コードは次のようにします。

    <v-img src="/images/sample-cover-art.png" />

これは「ビルドに含めるべきではない、http / https を通じてクライアントが参照すべき画像」を対象にすべきだと思います。私の場合は、Empty State 時に表示していたイラスト画像、ページ上部に配置していたカバーイラスト画像など、多くのものをこちらに変更しました。

データ定義用の json の中で require しているコードもあったのですが、これらもこのパターンで修正しました。

とある データ定義 json

{
    photo: require('@/assets/images/logo-pattern-a.png'),
    ...
}

修正後

{
    photo: '/images/logo-pattern-a.png',
    ...
}

build 後の js ではなく ts をそのまま import

これはちょっと特殊で、大抵の方には無関係な対応だと思います。

当社のモノレポでは共有コードを package のひとつとして管理しています。次のディレクトリ構造の shared がそれに該当し、共通のメッセージ定義や model、ビジネスロジック、ユーティリティコードなどがあります。

workspace
  - product-a
  - product-b
  - shared

この shared パッケージを yarn workspace 管理下において他のパッケージで利用するために、Webpack で CommonJS 形式に build していたのですが、Vite 後からは TypeScript のソースファイルを直接 import して利用できるようになりました。詳しくは次のリンク先に説明があります。

開発サーバを起動してみよう

気がせいている方はもうとっくに yarn dev なりを実行して開発サーバ起動しているかもしれませんが(笑)。ここまでくればエラーなく起動するはずです🎉

もし起動してエラーが出ていたら、エラーメッセージを確認しながらひとつずつ原因を潰してゆきましょう。

vue-cli 関連の除去

vue.config.js はもう使わないので、ファイルを削除しておきます。

パッケージ @vue-cli-service@vue-cli-plugin-* を削除します。対象は package.json を確認しましょう。

私の場合は次のとおりでした。

yarn remove @vue/cli-plugin-babel @vue/cli-plugin-eslint @vue/cli-plugin-router @vue/cli-service

おわりに

以上が Vite 化の主要な対応になると思います。

もし Vue 3 + Vuetify 3 への移行に脅えている方がいたら、このように Vite 化をまず終わらせてしまいましょう。開発サーバ起動とビルドが圧倒的に速くなる、この一点だけでも十分に価値があると思います。

ロンラン Tech Zenn

Discussion