Nuxt2 -> Nuxt3移行 nuxt-property-decoratorの依存の解消
nuxt-property-decorator(vue-property-decorator)
私の担当するNuxtのプロジェクトでは、vue-property-decorator及びnuxt-property-decoratorを使用して、Classベースで開発を進めることがほとんどでした。
簡単なページコンポーネントは例を挙げると以下のような感じになります
このプロジェクトをNuxt3にそのまま移行できるか、この記事では述べていきます
<template>
<ChildComponent :prop-value="propValue"></ChildComponent>
</template>
<script lang="ts">
import ChildComponent from "@/components/ChildComponent.vue";
import { Context } from "@nuxt/vue-app";
import { Component, Vue } from "nuxt-property-decorator";
@Component({
middleware: "auth",
components: { ChildComponent },
})
export default class extends Vue {
public propValue!: string;
private async asyncData(context: Context) {
return { propValue: "value" };
}
}
</script>
そもそもNuxt3(vue3)に移行するなら、全てのコンポーネントをComposition APIに置き換えるべきでは?みたいな意見があるとは思いますが、今回はなるべく少ない修正量で移行することを目的としているので、一旦考えないで進めます。トレードオフなどについては他の方が書かれているのでリンクだけ載せておきます。
vue-facing-decoratorへの置き換え
一旦nuxtについては置いておいて,nuxt-propert-decoratorが依存しているvue-property-decorator(vue-class-component)については、そのままではNuxt3(vue3)では動きません。
公式を見ると
This library is no longer actively maintained. If you still want to use classes, check out the community-maintained project vue-facing-decorator.
とあり、vue-facing-decoratorを使えとあります。
みたところ、ドキュメントも充実しているため、こちらに関しては安心して、importの部分だけ置き換えればひとまず動きそうです。
ただし、import部分以外の変更が必要なったことがあったので、Tipsとして書いておきます
PropSyncが存在しない
vue2時代には存在したpropSyncが動きませんでした。propSyncはv-model="xxx"
とv-on:update:xxxx
の書き方のショートハンドで割と簡潔に書けるので、多用していたのですが、vue3では現状サポートされていません。なので、当然vue-property-decoratorに存在した@PropSync
も存在しておらず、ショートハンドでない通常のEmit
の書き方に修正する必要があります。
// before
<child-component v-bind:name.sync="name"></child-component>
// after
<child-component v-bind:name="name" @update:name="name = $event"></child-component>
// before
<script lang="ts">
import { Component, PropSync, Vue } from "vue-property-decorator";
export default class ChildComponent extends Vue {
@PropSync("name") public syncedName!: string
onChangeName(newName: string) {
this.syncedName = newName
}
}
</script>
// after
<script lang="ts">
import { Component, Prop, Emit, Vue } from "vue-facing-decorator";
@Component({})
export default class ChildComponent extends Vue {
@Prop() public name!: string
onChageName(newName: string) {
this.$emit("update:name", newName)
}
// もしくはDecoratorを使った書き方
@Emit("update:name")
onChangeName(newName: string) {
return newName
}
}
</script>
this.$refs.xxx
がundefined
vue2では以下のようにrefsでアクセスできていたのですが、decoratorを使った書き方でないと動かなくなりました。
<template>
<div ref="refEl"></div>
</template>
<script langt="ts">
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
@Component({})
export default class Component extends Vue {
$refs: {
refEl: HTMLElement;
}
mounted() {
const element = this.$refs.refEl // <- 必ずundefinedになる
}
}
</script>
<template>
<div ref="refEl"></div>
</template>
<script langt="ts">
import { Component, Prop, Vue, Watch } from "vue-facing-decorator";
@Component({})
export default class Component extends Vue {
@Ref
readonly refEl!: HTMLElement
mounted() {
const element = this.refEl
}
}
</script>
nuxt-property-decoratorの代替
vue-property-decoratorだけだとnuxtのAPIにあるasyncData
やfetch
のhookやmiddleware
,layouts
の指定などができず、nuxt-property-decorator
を使う必要がありました。
しかし、nuxt-property-decoratorは以下のissueがcloseしていることからも、今後Nuxt3に対応する予定は残念ながらなさそうです。
一応nuxt3-class-componentがありましたが、asyncDataのみしか対応していないため現状ではとても移行には使えません。
ページコンポーネントに関してはComposition APIで書き直すのが現状では一番早そうです。
結論
- nuxt-property-decoratorの代替となるものは現状存在しない
- Composition APIで書き直した方が良い
- nuxtに依存しないコンポーネントであれば、vue-facing-decoratorが使える
- ただし一部propSync,
this.$refs
を使っていた箇所については修正する必要あり
- ただし一部propSync,
Compotision APIとClassComponentが混在するのはちょっと気持ち悪いですが、Class Componentを使用したNuxt2のプロジェクトの移行の工数を抑えたいのであれば、そこは割り切るしか現状はなさそうです。
Discussion