Vue.jsとvue-routerでページタイトルとメタデータを更新する方法
序章
vue-router はVue.jsの優れたルーティングソリューションですが、ルート変更時にページタイトルとメタデータを更新するために追加の構成が必要です。 ページが変更されたときにブラウザのタイトルを変更したい場合があります。 また、SEO(検索エンジン最適化)の場合、すべての検索結果やWebサイトへのリンクに、すべてのルートの「ホームページ」と表示する必要はありません。
この記事では、この機能を自分で追加する方法を学習します。 ルート変更時にカスタマイズ可能なページタイトルとメタデータを使用して、サンプルのVueアプリケーションを作成します。
前提条件
このチュートリアルを完了するには、次のものが必要です。
- Node.jsはローカルにインストールされます。これは、Node.jsのインストール方法とローカル開発環境の作成に従って実行できます。
このチュートリアルは、Node v14.6.0、npm v6.14.7、Vue.js v2.6.11、vue-router
v3.2.0、および@vue/cli
v4.4.6で検証されました。
ステップ1—Vueプロジェクトの作成と依存関係のインストール
新しいVueプロジェクトを作成しましょう。
まず、ターミナルを開き、vue-cliを使用してVueプロジェクトを作成します。
- 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)テストは必要ありません。
次に、新しいプロジェクトディレクトリに移動します。
- cd vue-router-meta-example
この時点で、構築する新しいVueプロジェクトがあります。 次のステップは、アプリケーションでサンプルルートを定義することです。 アプリケーションの構造を確立すると、title
とmeta
の動作の変化を確認できるようになります。
ステップ2—サンプルルートとテンプレートを定義する
この例では、次の要素で構成されるアプリケーションを構築することを目標としています。
- ホームルート(
/
) - 隣接するアバウトルート(
/about
) - ネストされたよくある質問のルート(
/about/frequently-asked-questions
)
次に、main.js
を開きます。
- nano src/main.js
VueRouter
がcli-plugin-router
によってどのように追加されたかを理解してください。
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
を開きます。
- nano src/router/index.js
cli-plugin-router
によって生成される"Home"
および"About"
のルートをよく理解してください。 そして、ネストされた"Frequently Asked Questions"
のルートを追加します。
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
という名前の新しいファイルを作成します。
- nano 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
を開きます。
- 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
を開きます。
- nano src/App.vue
cli-plugin-router
によってファイルがどのように変更されるかを理解してください。 そして、"Frequently Asked Questions"
に<router-link>
を追加します。
<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アプリケーションがあります。 次のコマンドを実行できます。
- npm run serve
そして、Webブラウザでlocalhost:8080
にアクセスします。 ナビゲーションリンクをクリックすると、予想されるコンポーネントが表示されます。 ただし、<title>
および<meta>
タグはまだ変更されていません。
ステップ3—ルートメタフィールドとナビゲーションガードを追加する
vue-router
は、title
およびmeta
値のルートメタフィールドをサポートします。 ルートをもう一度見て、メタフィールドを追加しましょう。
router/index.js
を開きます:
- nano src/router/index.js
そして、"Home"
、"About"
、および"Frequently Asked Questions"
のmeta
フィールドを追加します。
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
をエクスポートする前に、グローバルナビゲーションガードを追加します。
// ...
// 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アプリケーションができました。 次のコマンドを実行できます。
- npm run serve
そして、Webブラウザでlocalhost:8080
にアクセスします。 これで、ルートが変更されると、ページ<title>
が最も一致するルートのtitle
で更新されます。 同様に、<meta>
タグも更新されます。
結論
このチュートリアルでは、メタフィールドとナビゲーションガードを使用して、ルート変更時にページタイトルとメタデータを更新する方法を学習しました。
prerendering を使用する場合、これらの変更は事前にレンダリングされたHTMLファイルに組み込まれ、SEOに最適です。 SSR(サーバー側レンダリング)の場合、少し複雑になる可能性があります。
この方法では、動的で頻繁に更新されるタイトルは問題外であることにも注意してください。 このようなユースケースでは、おそらくdocument.title
を手動で更新する必要があります。
Vue.jsの詳細については、Vue.jsトピックページで演習とプログラミングプロジェクトを確認してください。