Vue 3 / Vuetify 3 に備えてまず Vite への移行をお勧めしたい
ロンラン株式会社 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/resolvers
のVuetifyResolver
-
unplugin-vue-components/vite
のComponents
これらがうまくブリッジしてくれるようです。
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
そのま使いたいんですけど!」という場合には、それもできそうです。
- https://ja.vitejs.dev/guide/env-and-mode.html#env-variables
- https://ja.vitejs.dev/config/index.html#環境変数
ただ、あまり Vite のスタンダードから逸脱しない方がよいと思いますし、Evan You のような天才が考えている設計思想は我々常人の考えの及ぶところではないので、『郷には入っては郷に従え』というのも大事かなと私は思います。
.vue
拡張子の補完
import 時の 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.png
を public/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 化をまず終わらせてしまいましょう。開発サーバ起動とビルドが圧倒的に速くなる、この一点だけでも十分に価値があると思います。
Discussion