💻

Nuxt.jsでOGP用のtitle/metaタグを設定する簡潔なコード

2020/03/14に公開

Nuxt.jsでOGP対応のためのtitle/metaタグを設定する方法を具体的に解説します。

できるだけ簡潔でメンテナンスしやすいコードになるように工夫しているので、参考にしていただければと思います。

Nuxt.jsにおけるtitle/metaタグの基本的な仕組み

Nuxt.jsでは、title/metaタグのコントロールは nuxt/vue-meta というライブラリに分離されています。

Nuxt.jsから使う場合、 SPAモードではなくUniversal(SSR)モードにしておかないと、ページごとのtitle/metaタグが反映されない ので注意が必要です。( nuxt generate するとしてもSPAモードだとページごとの変更は反映されない)

これからプロジェクトを作る場合は、最初の対話式コマンドでUniversalモードを選択するようにしてください。すでにプロジェクト作成済みの場合は、 nuxt.config.js でモードを確認・変更しておきましょう。

mode: 'universal',

さて、プロジェクト作成直後の nuxt.config.js には、以下のようなコードが書かれています。

head: {
  titleTemplate: '%s - ' + process.env.npm_package_name,
  title: process.env.npm_package_name || '',
  meta: [
    { charset: 'utf-8' },
    { name: 'viewport', content: 'width=device-width, initial-scale=1' },
    { hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
  ],
  link: [
    { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
  ]
},

これがコンパイルされると以下のようなHTMLコードになります。

<title>{package.jsonのname} - {package.jsonのname}</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta data-hid="description" name="description" content="{package.jsonのdescription}">
<link rel="icon" type="image/x-icon" href="/favicon.ico">

ということで、 nuxt.config.jshead を書けば「全ページ共通の」title/metaタグは簡単に設定できますね。

ちなみに、 description にだけ hid という属性が書かれていますが、これは子コンポーネントでmetaタグを上書きしたい場合に、上書き対象のmetaタグを一意に決めるためのキーとなる属性です。必要なければ書かなくても大丈夫です。

参考: https://ja.nuxtjs.org/faq/duplicated-meta-tags/

OGP対応として必要なtitle/metaタグと合わせて、最低限設定するべきは以下のような内容になるでしょう👌

head: {
  htmlAttrs: {
    prefix: 'og: http://ogp.me/ns#'
  },
  titleTemplate: 'ページのタイトルのテンプレート(%sの部分にtitleが埋め込まれる)',
  title: '{ページのタイトルの一部(テンプレートに埋め込まれる)}',
  meta: [
    { charset: 'utf-8' },
    { name: 'viewport', content: 'width=device-width, initial-scale=1' },
    { name: 'description', content: '{ページの説明}' },
    { property: 'og:site_name', content: '{サイト名}' },
    { property: 'og:type', content: 'website|article' },
    { property: 'og:title', content: '{ページのタイトル}' },
    { property: 'og:description', content: '{ページの説明}' },
    { property: 'og:url', content: '{ページの絶対URL}' },
    { property: 'og:image', content: '{アイキャッチ画像の絶対URL}' },
    { name: 'twitter:card', content: 'summary|summary_large_image' },
    { name: 'twitter:site', content: '@{Twitterユーザー名}' }
    { name: 'twitter:creator', content: '@{Twitterユーザー名}' }
    // 以下はなければ省略でOK
    { property: 'article:author', content: 'https://www.facebook.com/{Facebookユーザー名}' },
    { property: 'fb:admins', content: '{FacebookユーザーID}' },
    { property: 'fb:app_id', content: '{FacebookアプリID}' },
  ],
  link: [
    { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
  ]
},

参考:

ページごとにtitle/metaタグの内容を変える

さて、全ページ共通でtitle/metaタグを設定することは簡単にできそうですが、ページごとに内容を変えるにはどうすればよいのでしょうか。

最も簡単な方法は、各pagesのvueファイルで head の内容を上書きする、というものです。

例えば、 nuxt.config.js ではタイトル部分が以下のようになっているとして、

titleTemplate: '%s - サイト名',
title: '共通タイトル',
meta: [
    { hid: 'og:title', property: 'og:title', content: '共通タイトル - サイト名' },
],

<title> タグと og:title の内容をちゃんとページごとに変えたい場合は、以下のようにすれば上書きできます。

<!-- pages/price.vue -->
<template>
  <p>料金ページ</p>
</template>

<script>
export default {
  head: () => ({
    title: '料金ページ',
    meta: [
      { hid: 'og:title', property: 'og:title', content: '料金ページ - サイト名' }
    ]
  })
}
</script>

あるいは、トップページのタイトルを 共通タイトル - サイト名 ではなく単に サイト名 としたい場合なら、

<!-- pages/index.vue -->
<template>
  <p>トップページ</p>
</template>

<script>
export default {
  head: () => ({
    titleTemplate: '',
    title: 'サイト名',
    meta: [
      { hid: 'og:title', property: 'og:title', content: 'サイト名' }
    ]
  })
}
</script>

とすればOKです。

コンポーネントの head() メソッドで、上書きしたい値だけを返してあげればいいわけです。簡単ですね😉

ページによって変わる部分だけをmixin化して管理しやすくする

各コンポーネントから必要に応じて上書きできることは分かりましたが、

return {
  title: '料金ページ',
  meta: [
    { hid: 'og:title', property: 'og:title', content: '料金ページ - サイト名' }
  ]
}

とかをページごとに書くのは相当面倒くさいし、絶対ミスりそうですようね🙄

そこで、ページによって変わる部分だけを mixin として分離して、管理しやすくしたいと思います。

具体的には、プロジェクトルートに例えば mixins というディレクトリを作成し、その中に例えば meta.js というファイルを以下のような内容で作成します。

export default {
  data: () => ({
    meta: {
      title: null,
      description: 'デフォルトの説明',
      baseUrl: process.env.BASE_URL || 'http://localhost:3000',
      path: '/',
      ogType: 'website',
      ogImagePath: '/ogp.png'
    }
  }),
  head () {
    const title = this.meta.title ? `${this.meta.title} - サイト名` : 'サイト名'
    return {
      titleTemplate: '',
      title: title,
      meta: [
        { name: 'description', content: this.meta.description },
        { property: 'og:site_name', content: 'サイト名' },
        { property: 'og:type', content: this.meta.ogType },
        { property: 'og:title', content: title },
        { property: 'og:description', content: this.meta.description },
        { property: 'og:url', content: this.meta.baseUrl + this.meta.path },
        { property: 'og:image', content: this.meta.baseUrl + this.meta.ogImagePath },
        { property: 'article:author', content: 'https://www.facebook.com/{Facebookユーザー名}' },
        { property: 'fb:admins', content: '{FacebookユーザーID}' },
        { property: 'fb:app_id', content: '{FacebookアプリID}' },
        { name: 'twitter:card', content: 'summary_large_image' },
        { name: 'twitter:site', content: '@{Twitterユーザー名}' },
        { name: 'twitter:creator', content: '@{Twitterユーザー名}' }
      ]
    }
  }
}

このmixinでは、dataオプションで定義された変数を使ってtitle/metaタグの内容を定義しています。

後述しますが、ページごとに内容を変える際にはvue-metaの機能で上書きするのではなく、dataオプションで変数の値を変更して対応するので、 hid の指定は不要となっています👍

BASE_URL という環境変数を取得している箇所がありますが、これは、OGP系のmetaタグではURLをパスではなく絶対URLで記載する必要があるので、環境変数などを通して物理的に取得するしかありません。

本番環境では BASE_URL という環境変数に https://xxx.xxx のようなホスト名までのベースURLを設定しておきましょう。

このmixinを以下のようにしてページのコンポーネントにインジェクトすれば、まずはデフォルトの内容でtitle/metaタグが出力されます。

<!-- pages/index.vue -->
<template>
  <p>トップページ</p>
</template>

<script>
import Meta from '~/mixins/meta'
export default {
  mixins: [Meta]
}
</script>
<!-- pages/price.vue -->
<template>
  <p>料金ページ</p>
</template>

<script>
import Meta from '~/mixins/meta'
export default {
  mixins: [Meta]
}
</script>

ページごとに内容を変更するには、シンプルにdataオプションで必要な変数の値を上書きしてあげればよいです。

<!-- pages/index.vue -->
<template>
  <p>トップページ</p>
</template>

<script>
import Meta from '~/mixins/meta'
export default {
  mixins: [Meta]
  // トップページはデフォルトの内容でいいので特に何もしなくていい
}
</script>
<!-- pages/price.vue -->
<template>
  <p>料金ページ</p>
</template>

<script>
import Meta from '~/mixins/meta'
export default {
  mixins: [Meta],
  data: () => ({
    meta: {
      title: '料金ページ',
      description: '{料金ページの説明}',
      path: '/price/',
      ogImage: '{料金ページのOGP画像のパス}'
    }
  })
}
</script>

とってもシンプルですね!🎉

Vue.jsのmixin機能は、mixinのオプションとコンポーネントのオプションが重複した場合はコンポーネント側の値で上書きされる仕様 なので、このように

  • mixin側でデフォルトの値を設定しておく
  • コンポーネント側で変更したいものだけを設定する

という方法で上手いこと上書きできるのです👍

備忘録1:グローバルmixinにすれば各ページでのmixinの読み込みを省略できる?

どうせ全ページでmixinを読み込むのであれば、グローバルmixin を使って全コンポーネントに強制適用してしまってもいいのでは?と思い、こちらの記事 を参考に、以下のような方法も試してみました。

// mixins/meta.js
import Vue from 'vue'

Vue.mixin({
  data: () => ({
    // 略
  }),
  head () {
    // 略
  }
}
// nuxt.config.js
plugins: [
  '@/mixins/meta'
],

確かにこれで全ページにmixinを強制的に適用することができたのですが、 nuxt dev で開発中にmixinを修正してもコンパイル結果として反映されず、一度 nuxt dev を終了して再度実行しないと適用されませんでした🤔

原因が分かる方いらっしゃいましたら Twitter などでフィードバックをいただけると嬉しいです🙏

備忘録2:mixinで head () { return {} } の代わりに head: () => ({}) を使うと動かない

これもまったく理由が分からないのですが、mixinのコードを

head () {
  return {
    // 略
  }
}

から

head: () => ({
  // 略
})

という書き方にするだけで、dataオプションの中身にアクセスできなくなります😵

こちらも原因が分かる方いらっしゃいましたら Twitter などでフィードバックお待ちしています🙏

assets配下の画像ファイルを og:image に指定したい場合は?

assets配下の画像ファイルは /_nuxt/assets/{ファイル名} といったURLにビルドされますが、本番環境ではビルドの度にファイル名が変わってしまいます。

assetsではなくstatic配下の画像ファイルであれば、 /{ファイル名} でそのままアクセスできるので、OGP画像はassetsではなくstatic配下に置くようにすれば解決できます。

もしどうしてもassets配下の画像ファイルをOGP画像として使いたい場合は、以下のようにすればビルド後のファイルパスを取得できます👍

import ogImage from '@/assets/ogp.png'

export default {
  data: () => ({
    meta: {
      // 略
      ogImagePath: ogImage
    }
  }),
  head () {
    // 略
  }
}

参考: https://stackoverflow.com/questions/52683062/loading-ogimage-from-assets-in-nuxt-config-js

その他参考サイトなど

内容の基本的な部分は主にこちらの2記事を参考にしました。素晴らしい記事をありがとうございます!

蛇足ですが、title/metaタグの出力内容が正しいかどうかは以下のサイトでしっかり確認しましょうね!

まとめ

  • ページごとにtitle/metaタグを適切に出力するのはSEOに非常に重要!
  • Nuxt.jsをUniversalモードに設定しないとページごとに異なる内容を出力することはできないので要注意!
  • title/metaタグの設定内容はmixin化して使い回すとスマートにメンテナンスできる!
  • assets配下の画像ファイルを og:image にセットしたいときは画像を import すればよい!
GitHubで編集を提案

Discussion