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