Поиск по сайту:

Как обновить заголовок страницы и метаданные с помощью Vue.js и vue-router


Введение

vue-router — отличное решение для маршрутизации для Vue.js, но требует дополнительной настройки для обновления заголовка страницы и метаданных при изменении маршрута. Будут времена, когда вы захотите, чтобы заголовок браузера менялся при изменении страницы. А для SEO (поисковая оптимизация) вам не нужно, чтобы в каждом результате поиска или ссылке на ваш веб-сайт было указано «Домашняя страница» для всех маршрутов.

В этой статье вы узнаете, как добавить эту функцию самостоятельно. Вы создадите пример приложения 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:

  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, поддержка Progressive Web App (PWA), Vuex, препроцессоры CSS, модульное тестирование или сквозное (E2E) тестирование.

Затем перейдите в новый каталог проекта:

  1. cd vue-router-meta-example

На данный момент у нас есть свежий проект Vue, на который можно опираться. Следующим шагом будет определение пробных маршрутов в приложении. Как только мы установим структуру нашего приложения, мы сможем увидеть изменения title и meta в действии.

Шаг 2 — Определение пробных маршрутов и шаблонов

В нашем примере нашей целью будет создание приложения, состоящего из:

  • домашний маршрут (/)
  • соседний маршрут About (/about)
  • и вложенный маршрут часто задаваемых вопросов (/about/frequently-asked-questions)

Теперь откройте main.js:

  1. 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:

  1. nano src/router/index.js

Найдите минутку, чтобы ознакомиться с маршрутами для Home и About, сгенерированными cli-plugin-router. И добавьте маршрут для вложенных Часто задаваемых вопросов:

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

Это устанавливает нашу желаемую маршрутизацию для этого руководства. Обратите внимание, что мы ссылаемся на представление, которое еще не существует. Мы рассмотрим это далее.

Создайте новый файл с именем FrequentlyAskedQuestions.vue в каталоге views:

  1. 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:

  1. nano src/views/About.vue

Затем добавьте , чтобы вложенные маршруты отображали 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. И добавьте для Часто задаваемых вопросов:

<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>

На данный момент у нас есть приложение Vue с маршрутами к Главная, О программе и Часто задаваемые вопросы. Мы можем запустить следующую команду:

  1. npm run serve

И посетите localhost:8080 в веб-браузере. При нажатии на навигационные ссылки должны отображаться ожидаемые компоненты. Однако теги <title> и <meta> пока не меняются.

Шаг 3 — Добавление метаполей маршрута и навигационной защиты

vue-router поддерживает метаполя маршрута для значений title и meta. Давайте вернемся к нашим маршрутам и добавим метаполя.

Откройте router/index.js:

  1. nano src/router/index.js

И добавьте поля 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 с маршрутами, метаполями и защитой навигации. Мы можем запустить следующую команду:

  1. npm run serve

И посетите localhost:8080 в веб-браузере. Теперь при изменении ваших маршрутов страница <title> будет обновляться с указанием названия ближайшего соответствующего маршрута. Точно так же обновятся и теги <meta>.

Заключение

В этом руководстве вы узнали, как использовать метаполя и средства навигации для обновления заголовка страницы и метаданных при изменении маршрута.

Если вы используете предварительный рендеринг, то эти изменения будут встроены в ваши предварительно обработанные HTML-файлы и будут отлично работать для SEO. Для SSR (рендеринг на стороне сервера) это может быть немного сложнее.

Также стоит отметить, что при использовании этого метода не может быть и речи о динамических, часто обновляемых заголовках. Вам, вероятно, придется вручную обновлять document.title для таких случаев использования.

Если вы хотите узнать больше о Vue.js, посетите нашу тематическую страницу Vue.js, где вы найдете упражнения и проекты по программированию.