如何使用 Vue.js 和 vue-router 更新页面标题和元数据

介绍

vue-router是 Vue.js 的优秀路由解决方案,但需要额外的配置来更新页面标题和路由更改的元数据。有时您会希望浏览器的标题在页面更改时更改。对于 SEO(搜索引擎优化),您不会希望每个搜索结果或指向您网站的链接都为所有路线显示“主页”。

在本文中,您将学习如何自己添加此功能。您将构建一个示例 Vue 应用程序,其中包含可自定义的页面标题和有关路由更改的元数据。

先决条件

要完成本教程,您需要:

本教程已通过 Node v14.6.0、npm v6.14.7、Vue.js v2.6.11、vue-routerv3.2.0 和@vue/cliv4.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、Progressive Web App (PWA) 支持、Vuex、CSS 预处理器、单元测试或端到端 (E2E) 测试。

接下来,导航到新的项目目录:

  • cd vue-router-meta-example

在这一点上,我们有一个新的 Vue 项目可以构建。下一步将是在应用程序中定义示例路由。一旦我们建立了我们的应用程序的结构,我们将能够看到titlemeta变化的行动。

步骤 2 — 定义示例路由和模板

在我们的示例中,我们的目标是构建一个包含以下内容的应用程序:

  • 回家路线 ( /)
  • 相邻的 About 路线 ( /about)
  • 和嵌套的常见问题解答 ( /about/frequently-asked-questions)

现在,打开main.js

  • nano src/main.js

花点时间熟悉一下是如何VueRouter添加的cli-plugin-router

源代码/main.js
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

花点时间熟悉的路线"Home",并"About"通过产生cli-plugin-router并为嵌套添加路由"Frequently Asked Questions"

src/路由器/index.js
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.vueviews目录中创建一个名为的新文件

  • nano src/views/FrequentlyAskedQuestions.vue

然后,添加模板:

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. 并添加<router-link>"Frequently Asked Questions"

源代码/App.vue
<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公司的应用程序"Home""About""Frequently Asked Questions"我们可以运行以下命令:

  • npm run serve

localhost:8080在网络浏览器中访问单击导航链接应显示预期的组件。但是,<title><meta>标签还没有改变。

第 3 步 – 添加路由元字段和导航守卫

vue-router支持路由元字段titlemeta值。让我们重新审视我们的路线并添加元字段。

打开router/index.js:

  • nano src/router/index.js

并添加meta字段"Home""About"以及"Frequently Asked Questions"

src/路由器/index.js
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

源代码/路由/index.js
// ... 

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

localhost:8080在网络浏览器中访问现在,当您的路线发生变化时,页面<title>将更新为最近匹配的路线的title. 同样,<meta>标签也会更新。

结论

在本教程中,您学习了如何使用元字段和导航守卫在路由更改时更新页面标题和元数据。

如果您使用prerendering,那么这些更改将被烘焙到您的预渲染 HTML 文件中,并且非常适合 SEO。对于 SSR(服务器端渲染),它可能会更复杂一些。

还值得注意的是,使用这种方法无法获得动态的、经常更新的标题。document.title对于此类用例,您可能不得不坚持手动更新

如果您想了解有关 Vue.js 的更多信息,请查看我们的 Vue.js 主题页面以获取练习和编程项目。

觉得文章有用?

点个广告表达一下你的爱意吧 !😁