介绍
vue-router是Vue.js的一个很好的路由解决方案,但需要额外的配置来更新页面标题和路由变更的元数据。在某些时候,你会希望浏览器的标题在页面变更时发生变化。
在本文中,您将学习如何自己添加此功能. 您将构建一个示例视图应用程序,具有可自定义页面标题和路由更改的元数据。
前提条件
要完成本教程,您将需要:
- 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 进行验证。
步骤 1 — 创建视图项目和安装依赖性
让我们创建一个新鲜的Vue项目。
首先,打开终端并使用 vue-cli
创建一个Vue项目:
1npx @vue/[email protected] 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 设置的默认预设的一组。
1{
2 "useConfigFiles": false,
3 "plugins": {
4 "@vue/cli-plugin-babel": {},
5 "@vue/cli-plugin-eslint": {
6 "config": "base",
7 "lintOn": ["save"]
8 }
9 },
10 "router": true,
11 "routerHistoryMode": true
12}
这些预设将vue-router
添加为插件(cli-plugin-router
)(https://github.com/vuejs/vue-cli/tree/dev/packages/@vue/cli-plugin-router),启用 history mode,添加 Babel,并添加 ESLint。
对于本教程的需求,您将不需要TypesScript,进步Web App(PWA)支持,Vuex,CSS预处理器,单元测试或端到端(E2E)测试。
接下来,导航到新项目目录:
1cd vue-router-meta-example
在这一点上,我们有一个新的Vue项目要建立在基础上。下一步将是定义应用程序中的样本路线。一旦我们建立了应用程序的结构,我们将能够看到标题
和meta
在行动中的变化。
步骤 2 – 定义示例路径和模板
在我们的示例中,我们的目标将是构建一个由:
- 一个主路线(
/
) - 一个相邻的关于路线(
/about
) - 和一个嵌入的常见问题路线(
/about/frequently-asked-questions
)
现在,打开main.js
:
1nano src/main.js
花一会儿了解cli-plugin-router
如何添加VueRouter
:
1[label src/main.js]
2import Vue from 'vue'
3import App from './App.vue'
4import router from './router'
5
6Vue.config.productionTip = false
7
8new Vue({
9 router,
10 render: h => h(App)
11}).$mount('#app')
现在,打开路由器/index.js
:
1nano src/router/index.js
花一点时间来熟悉由cli-plugin-router生成的Home
和About
路线,并添加嵌入的常见问题
路线:
1[label src/router/index.js]
2import Vue from 'vue'
3import VueRouter from 'vue-router'
4import Home from '../views/Home.vue'
5import FrequentlyAskedQuestions from '../views/FrequentlyAskedQuestions.vue'
6
7Vue.use(VueRouter)
8
9const routes = [
10 {
11 path: '/',
12 name: 'Home',
13 component: Home
14 },
15 {
16 path: '/about',
17 name: 'About',
18 // route level code-splitting
19 // this generates a separate chunk (about.[hash].js) for this route
20 // which is lazy-loaded when the route is visited.
21 component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
22 children: [
23 {
24 path: 'frequently-asked-questions',
25 component: FrequentlyAskedQuestions,
26 }
27 ]
28 }
29]
30
31const router = new VueRouter({
32 mode: 'history',
33 base: process.env.BASE_URL,
34 routes
35})
36
37export default router
这为本教程建立了我们想要的路由。请注意,我们正在引用尚不存在的视图。
在视图
目录中创建一个名为FrequentlyAskedQuestions.vue
的新文件:
1nano src/views/FrequentlyAskedQuestions.vue
然后,添加寺庙:
1[label src/views/FrequentlyAskedQuestions.vue]
2<template>
3 <div>
4 <h2>Frequently Asked Questions</h2>
5 <dl>
6 <dt>What is your favorite aquatic animal?</dt>
7 <dd>Sharks.</dd>
8 <dt>What is your second favorite aquatic animal?</dt>
9 <dd>Dolphins.</dd>
10 <dt>What is your third favorite aquatic animal?</dt>
11 <dd>Cuttlefish.</dd>
12 </dl>
13 </div>
14</template>
15
16<style>
17dt {
18 font-weight: bold;
19}
20
21dd {
22 margin: 0;
23}
24</style>
我们有我们的新观点,但我们仍然需要在应用程序中参考它。
现在,打开About.vue
:
1nano src/views/About.vue
接下来,添加<路由器视图>
以便显示孩子
;
1<template>
2 <div class="about">
3 <h1>This is an about page</h1>
4 <router-view/>
5 </div>
6</template>
现在,打开App.vue
:
1nano src/App.vue
花一会儿来了解如何通过‘cli-plugin-router’修改文件,然后为‘常见问题’添加‘
1[label src/App.vue]
2<template>
3 <div id="app">
4 <div id="nav">
5 <router-link to="/">Home</router-link> |
6 <router-link to="/about">About</router-link> |
7 <router-link to="/about/frequently-asked-questions">FAQ</router-link>
8 </div>
9 <router-view/>
10 </div>
11</template>
在此时刻,我们有一个Vue应用程序,路由到Home
、About
和Frequently Asked Questions
。
1npm run serve
然后在网页浏览器中访问localhost:8080
。点击导航链接应该显示预期的组件,但是<title>
和<meta>
标签尚未改变。
步骤 3 — 添加路线元字段和导航警卫
vue-router
支持标题
和meta
值的 Route Meta Fields。
打开路由器/index.js
:
1nano src/router/index.js
并添加Meta
字段为家
、关于
和常见问题
:
1[label src/router/index.js]
2import Vue from 'vue'
3import VueRouter from 'vue-router'
4import Home from '../views/Home.vue'
5import FrequentlyAskedQuestions from '../views/FrequentlyAskedQuestions.vue'
6
7Vue.use(VueRouter)
8
9const routes = [
10 {
11 path: '/',
12 name: 'Home',
13 component: Home,
14 meta: {
15 title: 'Home Page - Example App',
16 metaTags: [
17 {
18 name: 'description',
19 content: 'The home page of our example app.'
20 },
21 {
22 property: 'og:description',
23 content: 'The home page of our example app.'
24 }
25 ]
26 }
27 },
28 {
29 path: '/about',
30 name: 'About',
31 // route level code-splitting
32 // this generates a separate chunk (about.[hash].js) for this route
33 // which is lazy-loaded when the route is visited.
34 component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
35 meta: {
36 title: 'About Page - Example App',
37 metaTags: [
38 {
39 name: 'description',
40 content: 'The about page of our example app.'
41 },
42 {
43 property: 'og:description',
44 content: 'The about page of our example app.'
45 }
46 ]
47 },
48 children: [
49 {
50 path: 'frequently-asked-questions',
51 component: FrequentlyAskedQuestions,
52 meta: {
53 title: 'Nested - About Page - Example App'
54 }
55 }
56 ]
57 }
58]
59
60const router = new VueRouter({
61 mode: 'history',
62 base: process.env.BASE_URL,
63 routes
64})
65
66export default router
但是,这不会导致页面标题和路径更改的元数据更新。
要做到这一点,我们将需要一个自定义(导航警卫)(LINK0)。
在route/index.js
文件中,在我们导出路由器
之前,在路由器之后添加一个全球导航卫士:
1[label src/route/index.js]
2// ...
3
4// This callback runs before every route change, including on page load.
5router.beforeEach((to, from, next) => {
6 // This goes through the matched routes from last to first, finding the closest route with a title.
7 // e.g., if we have `/some/deep/nested/route` and `/some`, `/deep`, and `/nested` have titles,
8 // `/nested`'s will be chosen.
9 const nearestWithTitle = to.matched.slice().reverse().find(r => r.meta && r.meta.title);
10
11 // Find the nearest route element with meta tags.
12 const nearestWithMeta = to.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);
13
14 const previousNearestWithMeta = from.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);
15
16 // If a route with a title was found, set the document (page) title to that value.
17 if(nearestWithTitle) {
18 document.title = nearestWithTitle.meta.title;
19 } else if(previousNearestWithMeta) {
20 document.title = previousNearestWithMeta.meta.title;
21 }
22
23 // Remove any stale meta tags from the document using the key attribute we set below.
24 Array.from(document.querySelectorAll('[data-vue-router-controlled]')).map(el => el.parentNode.removeChild(el));
25
26 // Skip rendering meta tags if there are none.
27 if(!nearestWithMeta) return next();
28
29 // Turn the meta tag definitions into actual elements in the head.
30 nearestWithMeta.meta.metaTags.map(tagDef => {
31 const tag = document.createElement('meta');
32
33 Object.keys(tagDef).forEach(key => {
34 tag.setAttribute(key, tagDef[key]);
35 });
36
37 // We use this to track which meta tags we create so we don't interfere with other ones.
38 tag.setAttribute('data-vue-router-controlled', '');
39
40 return tag;
41 })
42 // Add the meta tags to the document head.
43 .forEach(tag => document.head.appendChild(tag));
44
45 next();
46});
47
48// ...
在此时刻,我们有一个Vue应用程序,包括路线、元字段和导航卫士,我们可以运行以下命令:
1npm run serve
然后在网页浏览器中访问localhost:8080
。现在,当您的路线发生变化时,页面将更新为最接近匹配的路线的标题
。
结论
在本教程中,您了解了如何使用元字段和导航卫士来更新页面标题和路线更改的元数据。
如果你使用 prerendering,那么这些更改将被烤到你的预先分配的HTML文件中,并对SEO非常有效。
值得注意的是,动态的、经常更新的标题在这种方法中是没有问题的,您可能需要为此类用例手动更新document.title
。
如果您想了解有关 Vue.js 的更多信息,请参阅 我们的 Vue.js 主题页面以获取练习和编程项目。