🎅

Nuxt2 asyncData を Nuxt3 defineNuxtComponent で書き換え

2023/11/01に公開

Nuxt.js の v2→v3 のバージョンアップにおいて、asyncData の書き換え方がわからなかったのでまとめます。
当然 useAsyncData を使って書き換えられますし、時間があるならそっちの方がいいと思いますが、自分のチームでは書き換えコスト削減のため今回は見送る予定です。

Nuxt3 では defineNuxtComponent 内で asyncData を使うことができる

Nuxt3 の defineComponent では asyncData や head を使うことができませんが、
defineNuxtComponent 内ではこれらを使うことができます。(2023年10月時点)

defineNuxtComponent() is a helper function for defining type safe Vue components using options API similar to defineComponent(). defineNuxtComponent() wrapper also adds support for asyncData and head component options.

https://nuxt.com/docs/api/utils/define-nuxt-component

Nuxt3(defineNuxtComponent) の asyncData は引数に context を持たない

ただし、Nuxt2 の asyncData と全く同じではありません。
具体的には、asyncData の引数として渡すことができた context を、Nuxt3 では渡せませんでした。
(チームメンバーが教えてくれました)

Nuxt3 に移行するときの書き換え方

そこで、以降 context 依存のコードの書き換え方をまとめます。

基本

defineNuxtComponent を使います。

before

<script>
/* Nuxt2 */
export default {
  asyncData () {
    return {
      articles: [{
        id: 1, title: '楽しいクリスマス'
      }]
    }
  },
}
</script>

after

<script>
/* Nuxt3 */
export default defineNuxtComponent({
  asyncData () {
    return {
      articles: [{
        id: 1, title: '楽しいクリスマス'
      }]
    }
  },
})
</script>

error を使っている場合

before

<script>
/* Nuxt2 */
export default {
  asyncData ({ error }) {
    error({
      statusCode: 404,
      message: 'Page Not Found',
    })
  },
}
</script>

after

<script>
/* Nuxt3 */
export default defineNuxtComponent({
  asyncData () {
    showError({ 
      statusCode: 404, 
      statusMessage: 'Page Not Found' 
    })
  },
})
</script>

plugin(app) を使っている場合

Nuxt2 の asyncData では、引数の app から plugin を参照していました。
Nuxt3 では useNuxtApp を使います。

before

<script>
/* Nuxt2 */
export default {
  asyncData ({ app }) {
    const greet = app.$greet()
    return {
      greet,
    }
  },
}
</script>

after

<script>
/* Nuxt3 */
export default defineNuxtComponent({
  asyncData () {
    const nuxtApp = useNuxtApp()
    const greet = nuxtApp.$greet()
    return {
      greet,
    }
  },
})
</script>

store を使っている場合

vuex→pinia の書き換えも同時にやる場合で書いておきます。

before

<script>
/* Nuxt2 */
export default {
  asyncData ({ store }) {
    return {
      articles: store.state.articles,
    }
  },
}
</script>

after

<script>
/* Nuxt3 */
import { useArticleStore } from '~/stores/articleStore'

export default defineNuxtComponent({
  asyncData () {
    const store = useArticleStore()

    return {
      articles: store.articles,
    }
  },
})
</script>

params を使っている場合

before

<script>
/* Nuxt2 */
export default {
  asyncData ({ params }) {
    return {
      articleId: params.id,
    }
  },
}
</script>

after

<script>
/* Nuxt3 */
export default defineNuxtComponent({
  asyncData () {
    const route = useRoute()
    const articleId = route.params.id
    
    return {
      articleId,
    }
  },
})
</script>

query を使っている場合

before

<script>
/* Nuxt2 */
export default {
  asyncData ({ query }) {
    return {
      isPreview: query.preview === 'true',
    }
  },
}
</script>

after

<script>
/* Nuxt3 */
export default defineNuxtComponent({
  asyncData () {
    const route = useRoute()
    const isPreview = route.query.preview === 'true'
    
    return {
      isPreview,
    }
  },
})
</script>

redirect を使っている場合

before

<script>
/* Nuxt2 */
export default {
  asyncData ({ redirect }) {
    redirect('/articles')
  },
}
</script>

after

<script>
/* Nuxt3 */
export default defineNuxtComponent({
  asyncData () {
    navigateTo('/articles')
  },
})
</script>

【注意】 defineNuxtComponent の asyncData で return した値は、lang="ts" でも型補完が効かない

見出しの通り、defineNuxtComponent 内の asyncData で定義した値は、lang="ts" の場合でも型補完が効きませんでした。(こちらもチームメンバーの方が教えてくださいました)

プロパティ 'greeting' は型 '{ $: ComponentInternalInstance; $data: {}; $props: Partial<{}> & Omit<{} & VNodeProps & AllowedComponentProps & ComponentCustomProps & Readonly<...>, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infer R ? (args_0: R, args_1: R) => any : (...ar...' に存在しません。ts(2339)

useAsyncData で書き換えるまでは、lang="ts" 指定はしないか、 @ts-expect-error 等の対応が必要そうです。

Discussion