Pesquisa de site

Como atualizar o título da página e os metadados com Vue.js e vue-router


Introdução

vue-router é uma excelente solução de roteamento para Vue.js, mas requer configuração adicional para atualizar o título da página e os metadados na mudança de rota. Haverá momentos em que você desejará que o título do navegador seja alterado quando a página for alterada. E para SEO (otimização de mecanismo de busca), você não vai querer que cada resultado de busca ou link para seu site diga Página inicial para todas as rotas.

Neste artigo, você aprenderá como adicionar esse recurso por conta própria. Você criará um exemplo de aplicativo Vue com título de página personalizável e metadados na mudança de rota.

Pré-requisitos

Para concluir este tutorial, você precisará de:

  • Node.js instalado localmente, o que você pode fazer seguindo Como instalar o Node.js e criar um ambiente de desenvolvimento local.

Este tutorial foi verificado com Node v14.6.0, npm v6.14.7, Vue.js v2.6.11, vue-router v3.2.0 e @vue/cli v4. 4.6.

Etapa 1 — Criando um projeto Vue e instalando dependências

Vamos criar um novo projeto Vue.

Primeiro, abra seu terminal e use vue-cli para criar um projeto 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 longo é um conjunto de predefinições com base nos padrões estabelecidos por @vue/cli/packages/@vue/cli/lib/options.js. Quando reformatado para facilitar a leitura, fica assim:

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

Essas predefinições adicionam vue-router como um plug-in (cli-plugin-router), ativam o modo histórico, adicionam Babel e adicionam ESLint.

Para as necessidades deste tutorial, você não precisará de suporte a TypesScript, Progressive Web App (PWA), Vuex, pré-processadores CSS, teste de unidade ou teste de ponta a ponta (E2E).

Em seguida, navegue até o novo diretório do projeto:

  1. cd vue-router-meta-example

Neste ponto, temos um novo Projeto Vue para construir. A próxima etapa será definir rotas de amostra no aplicativo. Assim que tivermos estabelecido a estrutura de nosso aplicativo, poderemos ver as alterações de title e meta em ação.

Etapa 2 — Definindo Rotas e Modelos de Amostra

Em nosso exemplo, nosso objetivo será construir uma aplicação composta por:

  • uma rota para casa (/)
  • uma rota Sobre adjacente (/about)
  • e uma rota aninhada de perguntas frequentes (/about/frequently-asked-questions)

Agora, abra main.js:

  1. nano src/main.js

Reserve um momento para se familiarizar com a forma como VueRouter foi adicionado 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')

Agora, abra router/index.js:

  1. nano src/router/index.js

Reserve um momento para se familiarizar com as rotas para Home e About geradas por cli-plugin-router. E adicione a rota para as Perguntas Frequentes aninhadas:

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

Isso estabelece nosso roteamento desejado para este tutorial. Observe que estamos referenciando uma visão que ainda não existe. Trataremos disso a seguir.

Crie um novo arquivo chamado FrequentlyAskedQuestions.vue no diretório views:

  1. nano src/views/FrequentlyAskedQuestions.vue

Em seguida, adicione o modelo:

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

Temos nossa nova visualização, mas ainda precisamos fazer referência a ela no aplicativo.

Agora, abra About.vue:

  1. nano src/views/About.vue

Em seguida, adicione para que as rotas aninhadas exibam filhos;

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

Agora, abra App.vue:

  1. nano src/App.vue

Reserve um momento para se familiarizar com a forma como o arquivo é modificado pelo cli-plugin-router. E adicione para Perguntas frequentes:

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

Neste ponto, temos um aplicativo Vue com rotas para Home, About e Frequently Asked Questions. Podemos executar o seguinte comando:

  1. npm run serve

E visite localhost:8080 em um navegador da web. Clicar nos links de navegação deve exibir os componentes esperados. No entanto, as tags <title> e <meta> ainda não foram alteradas.

Etapa 3 — Adicionando metacampos de rota e um protetor de navegação

vue-router suporta Rota Meta Campos para valores title e meta. Vamos revisitar nossas rotas e adicionar metacampos.

Abra router/index.js:

  1. nano src/router/index.js

E adicione campos meta para Home, Sobre e Perguntas frequentes:

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

No entanto, isso não resultará na atualização do título da página e dos metadados na mudança de rota.

Para conseguir isso, precisaremos de um protetor de navegação personalizado.

No arquivo route/index.js, adicione um global navigation guard após as rotas, mas antes de exportarmos 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();
});

// ...

Neste ponto, temos um aplicativo Vue com rotas, metacampos e guarda de navegação. Podemos executar o seguinte comando:

  1. npm run serve

E visite localhost:8080 em um navegador da web. Agora, quando suas rotas mudarem, a página <title> será atualizada com o title da rota mais próxima. Da mesma forma, as tags <meta> também serão atualizadas.

Conclusão

Neste tutorial, você aprendeu como usar campos meta e protetores de navegação para atualizar o título da página e os metadados na mudança de rota.

Se você usar pré-renderização, essas alterações serão inseridas em seus arquivos HTML pré-renderizados e funcionarão muito bem para SEO. Para SSR (renderização do lado do servidor), pode ser um pouco mais complicado.

Também é importante notar que títulos dinâmicos e atualizados com frequência estão fora de questão com esse método. Você provavelmente terá que atualizar manualmente document.title para tais casos de uso.

Se você quiser aprender mais sobre Vue.js, confira nossa página de tópicos Vue.js para ver exercícios e projetos de programação.

Artigos relacionados: