序章

vue-router はVue.jsの優れたルーティングソリューションですが、ルート変更時にページタイトルとメタデータを更新するために追加の構成が必要です。 ページが変更されたときにブラウザのタイトルを変更したい場合があります。 また、SEO(検索エンジン最適化)の場合、すべての検索結果やWebサイトへのリンクに、すべてのルートの「ホームページ」と表示する必要はありません。

この記事では、この機能を自分で追加する方法を学習します。 ルート変更時にカスタマイズ可能なページタイトルとメタデータを使用して、サンプルのVueアプリケーションを作成します。

前提条件

このチュートリアルを完了するには、次のものが必要です。

このチュートリアルは、Node v14.6.0、npm v6.14.7、Vue.js v2.6.11、vue-router v3.2.0、および@vue/cliv4.4.6で検証されました。

ステップ1—Vueプロジェクトの作成と依存関係のインストール

新しいVueプロジェクトを作成しましょう。

まず、ターミナルを開き、vue-cliを使用してVueプロジェクトを作成します。

  1. npx @vue/cli@4.4.6 create --inlinePreset='{ "useConfigFiles": false, "plugins": { "@vue/cli-plugin-babel": {}, "@vue/cli-plugin-eslint": { "config": "base", "lintOn": ["save"] } }, "router": true, "routerHistoryMode": true }' vue-router-meta-example

この長いコマンドは、@vue/cli/packages/@vue/cli/lib/options.jsによって確立されたデフォルトに基づくプリセットのセットです。 読みやすさのために再フォーマットすると、次のようになります。

{
  "useConfigFiles": false,
  "plugins": {
    "@vue/cli-plugin-babel": {},
    "@vue/cli-plugin-eslint": {
      "config": "base",
      "lintOn": ["save"]
    }
  },
  "router": true,
  "routerHistoryMode": true
}

これらのプリセットは、プラグインとしてvue-routerを追加し( cli-plugin-router )、履歴モードを有効にし、Babelを追加し、ESLintを追加します。

このチュートリアルのニーズには、TypesScript、プログレッシブWebアプリ(PWA)のサポート、Vuex、CSSプリプロセッサ、単体テスト、またはエンドツーエンド(E2E)テストは必要ありません。

次に、新しいプロジェクトディレクトリに移動します。

  1. cd vue-router-meta-example

この時点で、構築する新しいVueプロジェクトがあります。 次のステップは、アプリケーションでサンプルルートを定義することです。 アプリケーションの構造を確立すると、titlemetaの動作の変化を確認できるようになります。

ステップ2—サンプルルートとテンプレートを定義する

この例では、次の要素で構成されるアプリケーションを構築することを目標としています。

  • ホームルート(/
  • 隣接するアバウトルート(/about
  • ネストされたよくある質問のルート(/about/frequently-asked-questions

次に、main.jsを開きます。

  1. nano src/main.js

VueRoutercli-plugin-routerによってどのように追加されたかを理解してください。

src / main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

次に、router/index.jsを開きます。

  1. nano src/router/index.js

cli-plugin-routerによって生成される"Home"および"About"のルートをよく理解してください。 そして、ネストされた"Frequently Asked Questions"のルートを追加します。

src / router / index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import FrequentlyAskedQuestions from '../views/FrequentlyAskedQuestions.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
    children: [
      {
        path: 'frequently-asked-questions',
        component: FrequentlyAskedQuestions,
      }
    ]
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

これにより、このチュートリアルに必要なルーティングが確立されます。 まだ存在していないビューを参照していることに注意してください。 次にそれについて説明します。

viewsディレクトリにFrequentlyAskedQuestions.vueという名前の新しいファイルを作成します。

  1. nano src/views/FrequentlyAskedQuestions.vue

次に、テンプレートを追加します。

src / views / FrequentlyAskedQuestions.vue
<template>
  <div>
    <h2>Frequently Asked Questions</h2>
    <dl>
      <dt>What is your favorite aquatic animal?</dt>
      <dd>Sharks.</dd>
      <dt>What is your second favorite aquatic animal?</dt>
      <dd>Dolphins.</dd>
      <dt>What is your third favorite aquatic animal?</dt>
      <dd>Cuttlefish.</dd>
    </dl> 
 </div>
</template>

<style>
dt {
  font-weight: bold;
}

dd {
  margin: 0;
}
</style>

新しいビューがありますが、それでもアプリケーションで参照する必要があります。

次に、About.vueを開きます。

  1. nano src/views/About.vue

次に、<router-view>を追加して、ネストされたルートchildrenを表示するようにします。

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <router-view/>
  </div>
</template>

次に、App.vueを開きます。

  1. nano src/App.vue

cli-plugin-routerによってファイルがどのように変更されるかを理解してください。 そして、"Frequently Asked Questions"<router-link>を追加します。

src / App.vue
<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link> |
      <router-link to="/about/frequently-asked-questions">FAQ</router-link>
    </div>
    <router-view/>
  </div>
</template>

この時点で、"Home""About"、および"Frequently Asked Questions"へのルートを持つVueアプリケーションがあります。 次のコマンドを実行できます。

  1. npm run serve

そして、Webブラウザでlocalhost:8080にアクセスします。 ナビゲーションリンクをクリックすると、予想されるコンポーネントが表示されます。 ただし、<title>および<meta>タグはまだ変更されていません。

ステップ3—ルートメタフィールドとナビゲーションガードを追加する

vue-routerは、titleおよびmeta値のルートメタフィールドをサポートします。 ルートをもう一度見て、メタフィールドを追加しましょう。

router/index.jsを開きます:

  1. nano src/router/index.js

そして、"Home""About"、および"Frequently Asked Questions"metaフィールドを追加します。

src / router / index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import FrequentlyAskedQuestions from '../views/FrequentlyAskedQuestions.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
    meta: {
      title: 'Home Page - Example App',
      metaTags: [
        {
          name: 'description',
          content: 'The home page of our example app.'
        },
        {
          property: 'og:description',
          content: 'The home page of our example app.'
        }
      ]
    }
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
    meta: {
      title: 'About Page - Example App',
      metaTags: [
        {
          name: 'description',
          content: 'The about page of our example app.'
        },
        {
          property: 'og:description',
          content: 'The about page of our example app.'
        }
      ]
    },
    children: [
      {
        path: 'frequently-asked-questions',
        component: FrequentlyAskedQuestions,
        meta: {
          title: 'Nested - About Page - Example App'
        }
      }
    ]
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

ただし、これにより、ルート変更時にページタイトルとメタデータが更新されることはありません。

これを実現するには、カスタムナビゲーションガードが必要です。

route/index.jsファイルで、ルートの後、routerをエクスポートする前に、グローバルナビゲーションガードを追加します。

src / route / index.js
// ... 

// This callback runs before every route change, including on page load.
router.beforeEach((to, from, next) => {
  // This goes through the matched routes from last to first, finding the closest route with a title.
  // e.g., if we have `/some/deep/nested/route` and `/some`, `/deep`, and `/nested` have titles,
  // `/nested`'s will be chosen.
  const nearestWithTitle = to.matched.slice().reverse().find(r => r.meta && r.meta.title);

  // Find the nearest route element with meta tags.
  const nearestWithMeta = to.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);

  const previousNearestWithMeta = from.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);

  // If a route with a title was found, set the document (page) title to that value.
  if(nearestWithTitle) {
    document.title = nearestWithTitle.meta.title;
  } else if(previousNearestWithMeta) {
    document.title = previousNearestWithMeta.meta.title;
  }

  // Remove any stale meta tags from the document using the key attribute we set below.
  Array.from(document.querySelectorAll('[data-vue-router-controlled]')).map(el => el.parentNode.removeChild(el));

  // Skip rendering meta tags if there are none.
  if(!nearestWithMeta) return next();

  // Turn the meta tag definitions into actual elements in the head.
  nearestWithMeta.meta.metaTags.map(tagDef => {
    const tag = document.createElement('meta');

    Object.keys(tagDef).forEach(key => {
      tag.setAttribute(key, tagDef[key]);
    });

    // We use this to track which meta tags we create so we don't interfere with other ones.
    tag.setAttribute('data-vue-router-controlled', '');

    return tag;
  })
  // Add the meta tags to the document head.
  .forEach(tag => document.head.appendChild(tag));

  next();
});

// ...

この時点で、ルート、メタフィールド、ナビゲーションガードを備えたVueアプリケーションができました。 次のコマンドを実行できます。

  1. npm run serve

そして、Webブラウザでlocalhost:8080にアクセスします。 これで、ルートが変更されると、ページ<title>が最も一致するルートのtitleで更新されます。 同様に、<meta>タグも更新されます。

結論

このチュートリアルでは、メタフィールドとナビゲーションガードを使用して、ルート変更時にページタイトルとメタデータを更新する方法を学習しました。

prerendering を使用する場合、これらの変更は事前にレンダリングされたHTMLファイルに組み込まれ、SEOに最適です。 SSR(サーバー側レンダリング)の場合、少し複雑になる可能性があります。

この方法では、動的で頻繁に更新されるタイトルは問題外であることにも注意してください。 このようなユースケースでは、おそらくdocument.titleを手動で更新する必要があります。

Vue.jsの詳細については、Vue.jsトピックページで演習とプログラミングプロジェクトを確認してください。