Skip to content

Commit 11b8a5d

Browse files
committed
feat(router): add ability to enter custom title and meta info
1 parent 2ec6c57 commit 11b8a5d

File tree

6 files changed

+154
-5
lines changed

6 files changed

+154
-5
lines changed

public/test.png

37.2 KB
Loading

src/router/index.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const RouterLayout = createRouterLayout(layout => {
77
return import("@/views/layouts/" + layout + ".vue");
88
});
99

10-
export default createRouter({
10+
const router = createRouter({
1111
history: createWebHistory(process.env.BASE_URL),
1212
routes: [
1313
{
@@ -17,3 +17,43 @@ export default createRouter({
1717
}
1818
]
1919
});
20+
21+
22+
router.beforeEach((to, from, next) => {
23+
// This goes through the matched routes from last to first, finding the closest route with a title.
24+
// eg. if we have /some/deep/nested/route and /some, /deep, and /nested have titles, nested's will be chosen.
25+
const nearestWithTitle = to.matched.slice().reverse().find(r => r.meta && r.meta.title);
26+
27+
// Find the nearest route element with meta tags.
28+
const nearestWithMeta = to.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);
29+
// const previousNearestWithMeta = from.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);
30+
31+
// If a route with a title was found, set the document (page) title to that value.
32+
if(nearestWithTitle) document.title = nearestWithTitle.meta.title;
33+
34+
// Remove any stale meta tags from the document using the key attribute we set below.
35+
Array.from(document.querySelectorAll('[data-vue-router-controlled]')).map(el => el?.parentNode?.removeChild(el));
36+
37+
// Skip rendering meta tags if there are none.
38+
if(!nearestWithMeta) return next();
39+
40+
// Turn the meta tag definitions into actual elements in the head.
41+
nearestWithMeta.meta.metaTags.map((tagDef: { [x: string]: string; }) => {
42+
const tag = document.createElement('meta');
43+
44+
Object.keys(tagDef).forEach(key => {
45+
tag.setAttribute(key, tagDef[key]);
46+
});
47+
48+
// We use this to track which meta tags we create, so we don't interfere with other ones.
49+
tag.setAttribute('data-vue-router-controlled', '');
50+
51+
return tag;
52+
})
53+
// Add the meta tags to the document head.
54+
.forEach((tag: Node) => document.head.appendChild(tag));
55+
56+
next();
57+
});
58+
59+
export default router;

src/views/pages/docs/install.vue

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<DocHeader>
33
<template #title> Installation </template>
4-
<template #subtitle> Get started in seconds </template>
4+
<template #subtitle>Get started in Vue Zephyr in minutes.</template>
55
<template #right-side>
66
<div :class="tw`justify-end flex items-start md:mt-2 space-x-2`">
77
<Badge classAppend="bg-yellow-500 text-gray-900">In Progress</Badge>
@@ -127,4 +127,35 @@ export default defineComponent({
127127
return { codes, tw };
128128
},
129129
});
130-
</script>
130+
</script>
131+
132+
<route>
133+
{
134+
"name": "install",
135+
"meta": {
136+
"title": "Installation | Vue Zephyr",
137+
"metaTags": [
138+
{
139+
"property": "og:title",
140+
"content": "Installation"
141+
},
142+
{
143+
"name": "description",
144+
"content": "Get started in Vue Zephyr in minutes."
145+
},
146+
{
147+
"property": "og:description",
148+
"content": "Get started in Vue Zephyr in minutes."
149+
},
150+
{
151+
"property": "og:url",
152+
"content": "https://usezephyr.com/docs/install"
153+
},
154+
{
155+
"property": "og:url",
156+
"content": "https://usezephyr.com/test.png"
157+
}
158+
]
159+
}
160+
}
161+
</route>

src/views/pages/docs/starter.vue

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<template>
2+
<div></div>
3+
</template>
4+
5+
<script lang="ts">
6+
import { defineComponent } from "vue";
7+
8+
export default defineComponent({
9+
setup() {
10+
return {};
11+
},
12+
});
13+
</script>
14+
15+
<route>
16+
{
17+
"name": "starter",
18+
"meta": {
19+
"title": "Starter | Vue Zephyr",
20+
"metaTags": [
21+
{
22+
"property": "og:title",
23+
"content": "Starter"
24+
},
25+
{
26+
"name": "description",
27+
"content": "This is a starter page for the docs. You should not be here."
28+
},
29+
{
30+
"property": "og:description",
31+
"content": "This is a starter page for the docs. You should not be here."
32+
},
33+
{
34+
"property": "og:url",
35+
"content": "https://usezephyr.com/docs/starter"
36+
},
37+
{
38+
"property": "og:url",
39+
"content": "https://usezephyr.com/test.png"
40+
}
41+
]
42+
}
43+
}
44+
</route>

src/views/pages/docs/theming.vue

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,33 @@ export default defineComponent({
3232
});
3333
</script>
3434

35-
<style scoped>
36-
</style>
35+
<route>
36+
{
37+
"name": "theming",
38+
"meta": {
39+
"title": "Themeing | Vue Zephyr",
40+
"metaTags": [
41+
{
42+
"property": "og:title",
43+
"content": "Theming"
44+
},
45+
{
46+
"name": "description",
47+
"content": "Customize the apperance for your project."
48+
},
49+
{
50+
"property": "og:description",
51+
"content": "Customize the apperance for your project."
52+
},
53+
{
54+
"property": "og:url",
55+
"content": "https://usezephyr.com/docs/theming"
56+
},
57+
{
58+
"property": "og:url",
59+
"content": "https://usezephyr.com/test.png"
60+
}
61+
]
62+
}
63+
}
64+
</route>

vue.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,11 @@ module.exports = {
1717
},
1818
chainWebpack: (config) => {
1919
addVueRawPreLoader(config);
20+
config
21+
.plugin('html')
22+
.tap(args => {
23+
args[0].title = 'Vue Zephyr'
24+
return args
25+
})
2026
},
2127
};

0 commit comments

Comments
 (0)