Help us understand the problem. What is going on with this article?

Nuxt.js(Vue) apiで取得した画像やデータで動的にOGP設定を変える方法

Nuxt.js(Vue)でOGP設定をapiで取得した画像やデータで、ページごとに動的に変えたい時の方法です。

一般的なNuxt.jsでのOGP設定については、下記の記事にまとめています。

【Nuxt.jsでOGP設定をする方法】

mountedではmetaタグは書き換えられるけどOGPのクロールボットは読みにこれない

下記のように、mounted()でapiを取得して、OGPを動的に書き換えようとしても上手く行きませんでした。

<script lang="ts">
import Vue from 'vue'
import axios from 'axios'
export default Vue.extend({
  data() {
    return {
      userId: 0,
      profilePicture: '',
      comment: '',
    }
  },
  mounted() {
    this.profileGet()
  },
  methods: {
    async profileGet(): Promise<void> {
      const response = await axios
        .get('/api/profile/search', {
          params: {
            id: this.$route.params.id
          }
        })
        .catch((error) => error.response)
      if (
        response.data.status !== 'success' ||
        response.data.data.data.length <= 0
      ) {
        this.$router.push('/')
      }
      this.setData(response.data.data.data[0])
    },
    setData(data: any) {
      this.userId = Number(this.$route.params.id)
      this.profilePicture = String(data.profile_picture)
      this.comment = String(data.comment)
    }
  },
  head(): any {
    return {
      meta: [
        {
          hid: 'og:description',
          property: 'og:description',
          content: this.comment
        },
        {
          hid: 'og:url',
          property: 'og:url',
          content: this.snsUrl
        },
        {
          hid: 'og:image',
          property: 'og:image',
          content: this.profilePicture
        }
      ]
    }
  }
})
</script>

metaタグ自体はしっかりと書き換わるのですが、OGPをクロールするボットはクライアント側の処理を汲み取ってくれないようです。その為、twitterなどでシェアしようとしても画像は表示されません。

mountedのライフサイクルのタイミングはDOMが作成された直後の状態です。画面表示の為のapi取得では問題ないですが、OGPを動的に変える場合は他のライフサイクルを使う必要があります。

asyncDataでapiを取得することで解決

Nuxt.jsにはasyncDataメソッドがあり、サーバーサイドレンダリングすることができます。

asyncData はコンポーネントがロードされる前に毎回呼び出されます。サーバーサイドレンダリングや、ユーザーがページを遷移する前にも呼び出されます。
下記の公式サイトに詳しく説明が載っています。

asyncData メソッド

moutendからasyncDataにapiの取得タイミングを変更しました。

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  userId: 0
  profilePicture: '',
  comment: '',

  async asyncData(context: any) {
    if (process.client) {
      return {
        userId: 0,
        profilePicture: '',
        comment: ''
      }
    }
    try {
      const response = await context.$axios
        .get('/api/profile/search', {
          params: {
            param: context.params.id
          }
        })
        .catch((error: any) => error.response)

      if (
        response.data.status !== 'success' ||
        response.data.data.data.length <= 0
      ) {
        context.redirect('/top')
      }

      return {
        userId: Number(context.params.id),
        profilePicture: String(response.data.data.data[0].profilePicture),
        comment: String(response.data.data.data[0].comment)
      }
    } catch (error) {
      return {
        userId: 0,
        profilePicture: '',
        comment: ''
      }
    }
  },
  head(): any {
    return {
      meta: [
        {
          hid: 'og:description',
          property: 'og:description',
          content: this.comment
        },
        {
          hid: 'og:url',
          property: 'og:url',
          content: this.snsUrl
        },
        {
          hid: 'og:image',
          property: 'og:image',
          content: this.profilePicture
        }
      ]
    }
  }
})
</script>

asyncDataメソッドでapiを取得することで、OGPも動的に書き換えることができました。
ユーザー情報やブログの記事のようにページごとにOGPを変化させたい時は、asyncDataを使うのがベストかなと思います。

*asyncData()はページコンポーネントのみで使用できます。子コンポーネントでは使用できない点には注意が必要です。

TK-C
Frontend/WebDesign
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away