Búsqueda de sitios web

Cómo actualizar el título de la página y los metadatos con Vue.js y vue-router


Introducción

vue-router es una excelente solución de enrutamiento para Vue.js, pero requiere una configuración adicional para actualizar el título de la página y los metadatos en el cambio de ruta. Habrá momentos en los que querrá que el título del navegador cambie cuando cambie la página. Y para SEO (optimización de motores de búsqueda), no querrá que todos los resultados de búsqueda o enlaces a su sitio web digan \Página de inicio para todas las rutas.

En este artículo, aprenderá cómo agregar esta función usted mismo. Creará una aplicación Vue de ejemplo con un título de página personalizable y metadatos sobre el cambio de ruta.

requisitos previos

Para completar este tutorial, necesitará:

  • Node.js instalado localmente, lo que puede hacer siguiendo Cómo instalar Node.js y crear un entorno de desarrollo local.

Este tutorial se verificó con Node v14.6.0, npm v6.14.7, Vue.js v2.6.11, vue-router v3.2.0 y @vue/cli v4. 4.6.

Paso 1: crear un proyecto Vue e instalar dependencias

Vamos a crear un nuevo proyecto Vue.

Primero, abra su terminal y use vue-cli para crear un proyecto 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

Este comando largo es un conjunto de ajustes preestablecidos basados en valores predeterminados establecidos por @vue/cli/packages/@vue/cli/lib/options.js. Cuando se reformatea para facilitar la lectura, se ve así:

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

Estos ajustes preestablecidos agregan vue-router como un complemento (cli-plugin-router), habilitan el modo de historial, agregan Babel y agregan ESLint.

Para las necesidades de este tutorial, no necesitará TypesScript, compatibilidad con Progressive Web App (PWA), Vuex, preprocesadores de CSS, pruebas unitarias o pruebas de extremo a extremo (E2E).

A continuación, navegue hasta el nuevo directorio del proyecto:

  1. cd vue-router-meta-example

En este punto, tenemos un Proyecto Vue nuevo sobre el que construir. El siguiente paso será definir rutas de muestra en la aplicación. Una vez que hayamos establecido la estructura de nuestra aplicación, podremos ver los cambios title y meta en acción.

Paso 2: Definición de rutas y plantillas de muestra

En nuestro ejemplo, nuestro objetivo será construir una aplicación que consista en:

  • una ruta de inicio (/)
  • una ruta Acerca de adyacente (/about)
  • y una ruta anidada de preguntas frecuentes (/about/frequently-asked-questions)

Ahora, abre main.js:

  1. nano src/main.js

Tómese un momento para familiarizarse con cómo VueRouter ha sido agregado por 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')

Ahora, abre router/index.js:

  1. nano src/router/index.js

Tómese un momento para familiarizarse con las rutas para Home y About generadas por cli-plugin-router. Y agregue la ruta para las Preguntas frecuentes anidadas:

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

Esto establece nuestro enrutamiento deseado para este tutorial. Tenga en cuenta que estamos haciendo referencia a una vista que aún no existe. Abordaremos eso a continuación.

Cree un nuevo archivo llamado FrequentlyAskedQuestions.vue en el directorio views:

  1. nano src/views/FrequentlyAskedQuestions.vue

Luego, agregue la plantilla:

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

Tenemos nuestra nueva vista, pero aún necesitamos hacer referencia a ella en la aplicación.

Ahora, abre About.vue:

  1. nano src/views/About.vue

A continuación, agregue para que las rutas anidadas muestren child;

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

Ahora, abre App.vue:

  1. nano src/App.vue

Tómese un momento para familiarizarse con la forma en que cli-plugin-router modifica el archivo. Y agregue para Preguntas frecuentes:

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

En este punto, tenemos una aplicación Vue con rutas a Home, About y Frequently Asked Questions. Podemos ejecutar el siguiente comando:

  1. npm run serve

Y visita localhost:8080 en un navegador web. Al hacer clic en los enlaces de navegación, se deben mostrar los componentes esperados. Sin embargo, las etiquetas <title> y <meta> aún no están cambiando.

Paso 3: agregar metacampos de ruta y un protector de navegación

vue-router admite campos meta de ruta para valores title y meta. Revisemos nuestras rutas y agreguemos metacampos.

Abra router/index.js:

  1. nano src/router/index.js

Y agregue los campos meta para Home, About y 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,
    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

Sin embargo, esto no resultará en la actualización del título de la página y los metadatos en el cambio de ruta.

Para lograr eso, necesitaremos un protector de navegación personalizado.

En el archivo route/index.js, agregue un protector de navegación global después de las rutas pero antes de exportar 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();
});

// ...

En este punto, tenemos una aplicación Vue con rutas, metacampos y protección de navegación. Podemos ejecutar el siguiente comando:

  1. npm run serve

Y visita localhost:8080 en un navegador web. Ahora, cuando sus rutas cambien, la página <title> se actualizará con el title de la ruta más parecida. Asimismo, las etiquetas <meta> también se actualizarán.

Conclusión

En este tutorial, aprendió a usar metacampos y protecciones de navegación para actualizar el título de la página y los metadatos en el cambio de ruta.

Si utiliza la renderización previa, estos cambios se incluirán en sus archivos HTML renderizados previamente y funcionarán muy bien para SEO. Para SSR (representación del lado del servidor), puede ser un poco más complicado.

También vale la pena señalar que los títulos dinámicos que se actualizan con frecuencia están fuera de discusión con este método. Probablemente tendrá que seguir actualizando manualmente document.title para tales casos de uso.

Si desea obtener más información sobre Vue.js, consulte nuestra página de temas de Vue.js para ver ejercicios y proyectos de programación.

Artículos relacionados: