はじめに
こんにちは🧑💻 ラボルのUIデザイナー/フロントエンドエンジニアの寺岡です。
今回はVue開発者向けの記事になります。
Vue2のバグ修正・セキュリティアップデートが、2023年12月31日に終了することをご存知でしょうか?
これ受けて、ラボルのフロントエンドチームは、Vue2→Vue3にアップデートする移行作業をこの半年間実施していました。
今回の記事は、その移行作業の目的や手順、2→3の変更点など、これから作業する方に向けたものになります。是非ご一読ください。
目次
Vue3移行の目的
最も重要な目的は、上述している「バグ修正・セキュリティアップデートが、2023年12月31日に終了すること」がメインの目的ですが、ラボルでは他に以下のサブ目的も定義していました。
- 機能の再利用を可能に
- コードの可読性の向上
これらはVue3から使用可能なComposition APIの記述をすることによって得られる恩恵です。
Composition APIのメリットについて詳しく知りたい方は、以下の記事もオススメです!
Vue2時点ではComposition APIがメインで提供されていなかったですが、Vue3ではOptions API・Composition API両方の選択肢があることは嬉しいですね。
Vue3移行の手順
ラボルでのVue3移行は、ざっくり以下のような手順で行いました。
- Vue3(新)プロジェクトを新規作成
- Vue2(旧)プロジェクトのコードを、Vue3プロジェクトにコピペ
- Vue3プロジェクトの方で、コピペしたコードをComposition APIの形にリファクタリング
- 既存仕様に変更がないことをテストしリリース
この手順を、ページ単位でのリリースを繰り返して行っていました。
1と2に関して、以下のようなイメージのディレクトリ構成になっています。
vue3-project/
が新しく作成した環境になります。
. ├── vue2-project/ │ ├── assets │ ├── components │ ├── pages │ └── lib └── vue3-project/ ├── assets ├── components ├── pages └── lib
3の部分を具体的にコード例で説明していきます。
Vue2(旧)コード
以下のようなページがあるとします。
※CSSは省略しています。
<template> <h1>{{ memberName }}さんのマイページ</h1> <p>申請可能額: ¥{{ member.availableCredit }}</p> <button @click="moveFormPage">申請ページへ</button> </template> <script> export default { data: { // memberの情報 member: { lastName: "", firstName: "", availableCredit: 0, }, }, computed: { memberName() { return member.lastName + member.firstName }, }, async created() { await this.getMemberInfo() }, methods: { // member情報の取得 async getMemberInfo() { const res = await axios.get("endpoint") this.member.lastName = res.data.lastName this.member.firstName = res.data.firstName this.member.availableCredit = res.data.availableCredit }, // ページ遷移 moveFormPage() { this.$router.push("/mypage/form") }, }, } </script>
Vue3(新)コードでリファクタリング
上記のコードをコピペし、Composition APIの形に書き換えます。
※Options APIのままでも、問題はないです。
<template> <h1>{{ memberName }}さんのマイページ</h1> <p>申請可能額: ¥{{ member.availableCredit }}</p> <button @click="moveFormPage">申請ページへ</button> </template> <script setup> import { reactive, computed } from "vue" // memberの情報 const member = reactive({ lastName: "", firstName: "", availableCredit: 0, }) const memberName = computed(() => { return member.lastName + member.firstName }) // member情報を取得 const getMemberInfo = async() => { const res = await axios.get("endpoint") member.lastName = res.data.lastName member.firstName = res.data.firstName member.availableCredit = res.data.availableCredit } await getMemberInfo() // ページ遷移 const router = useRouter() const moveFormPage = () => { router.push("/mypage/form") } </script>
こうなると、TypeScriptの導入もしやすいですね。
tsの記述については、公式の以下のページがわかりやすいです。
<template> // 省略 </template> <script setup lang="ts"> // ←tsの使用を宣言 // memberの型 interface MemberType { lastName: string firstName: string availableCredit: number } const member: MemberType = reactive({ lastName: "", firstName: "", availableCredit: 0, }) // 戻り値stringのcomputed const memberName = computed<string>(() => { return member.lastName + member.firstName }) // void型の関数 const getMemberInfo = async(): void => { const res = await axios.get("endpoint") member.lastName = res.data.lastName member.firstName = res.data.firstName member.availableCredit = res.data.availableCredit } await getMemberInfo() </script>
Vue3移行の注意点
破壊的変更など、ラボルの移行作業の中で引っかかった項目を、注意喚起の意味も込めて紹介します。 また、破壊的変更(breaking-changes)は公式にてリストアップされているので、要チェックです。
ページ遷移
移行作業中は、どうしてもVue2上で動くページとVue3上で動くページが混在してしまいます。
ラボルはSPAなので、ページ遷移方法に気をつけないと、遷移先が404になってしまします。
遷移方法は以下4パターンがあります。
- Vue3→Vue3のページに遷移する時
<router-link to="">
(template内)router.push
(script内)
- Vue3→Vue2のページに遷移する時
<a href="">
(template内)window.location.href
(script内)
- Vue2→Vue3のページに遷移する時
<a href="">
(template内)window.location.href
(script内)
- Vue2→Vue2のページに遷移する時
<router-link to="">
(template内)router.push
(script内)
まとめると以下のような感じです。
遷移元→遷移先 | →Vue3 | →Vue2 |
---|---|---|
Vue3→ | 1. router遷移 | 2. aタグ遷移 |
Vue2→ | 3. aタグ遷移 | 4. router遷移 |
注意すべきは2と3で、要するに違うプロジェクトに遷移するケースを洗い出し、それらのテストが必要でした。
移行のリリース毎に、遷移テストを必須にし、遷移先が404になってしまうリスクを排除しました。
オブジェクトや配列のデータ更新
昔からVue2で開発していたフロントエンドの方はご存知だと思いますが、オブジェクトや配列のデータ更新をした時に、リアクティブになるよう以下の記述方法が必要でした。
リアクティブの探求 (Vue2時点のドキュメント)
// Vue2 // objectの更新 this.$set(obj, "newKey", "newItem") // -> obj: {newKey: "newItem"} // arrayの更新 this.$set(ary, 0, "newItem") // -> ary: ["newItem"]
このグローバル関数の$set
が、Vue3では削除されています。
Vue2での書き方に慣れている方こそ、引っかかるポイントだと思います。
グローバル関数の set と delete、インスタンスメソッドの $set と $delete は削除されました。プロキシベースの変更検出では不要になりました。
なので、以下の更新方法で問題ないです(リアクティブになります)
// Vue3 // objectの更新 obj.newKey = "newItem" // arrayの更新 ary[0] = "newItem"
<template v-for>
でのkey
の使い方
<template>
でv-for
を使う場合、Vue2ではkey
を子要素に設定していました。
<!-- Vue2 --> <template v-for="item in list"> <div :key="item.id">{{ item.name }}</div> </template>
しかし、Vue3ではkey
を<template>
に設定しなければいけません。
<!-- Vue3 --> <template v-for="item in list" :key="item.id"> <div>{{ item.name }}</div> </template>
ややこしいですね。
まとめ
Vue2で開発中のみなさん、年末の大掃除だと思って、2023年12月31日までにVue3にアップデートしましょう。
上述したラボルのVue3以降はリファクタリングも行っていましたが、最低限Options APIの記述のままでも問題ないです。
ただ、以下に移行時の注意点まとめたので、こちらだけ気をつけてください。
最後に
ラボルでは、エンジニアを積極採用中です。1、2年目のエンジニアから経験豊富なテックリードやエンジニリングマネージャーまで、興味がある方はぜひご応募ください!!