🍣

Nuxt2 -> Nuxt3移行 nuxt-property-decoratorの依存の解消

2023/08/06に公開

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に置き換えるべきでは?みたいな意見があるとは思いますが、今回はなるべく少ない修正量で移行することを目的としているので、一旦考えないで進めます。トレードオフなどについては他の方が書かれているのでリンクだけ載せておきます。

https://www.ragate.co.jp/blog/articles/8703

vue-facing-decoratorへの置き換え

一旦nuxtについては置いておいて,nuxt-propert-decoratorが依存しているvue-property-decorator(vue-class-component)については、そのままではNuxt3(vue3)では動きません。
公式を見ると

https://github.com/kaorun343/vue-property-decorator

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を使えとあります。

https://github.com/facing-dev/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を使った書き方でないと動かなくなりました。

pages/samplle.vue(before)
<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>
pages/samplle.vue(after)
<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にあるasyncDatafetchのhookやmiddleware,layoutsの指定などができず、nuxt-property-decoratorを使う必要がありました。
しかし、nuxt-property-decoratorは以下のissueがcloseしていることからも、今後Nuxt3に対応する予定は残念ながらなさそうです。

https://github.com/nuxt-community/nuxt-property-decorator/issues/83

一応nuxt3-class-componentがありましたが、asyncDataのみしか対応していないため現状ではとても移行には使えません。
ページコンポーネントに関してはComposition APIで書き直すのが現状では一番早そうです。

結論

  • nuxt-property-decoratorの代替となるものは現状存在しない
    • Composition APIで書き直した方が良い
  • nuxtに依存しないコンポーネントであれば、vue-facing-decoratorが使える
    • ただし一部propSync,this.$refsを使っていた箇所については修正する必要あり

Compotision APIとClassComponentが混在するのはちょっと気持ち悪いですが、Class Componentを使用したNuxt2のプロジェクトの移行の工数を抑えたいのであれば、そこは割り切るしか現状はなさそうです。

Discussion