forked from vuejs/vuepress
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathupdateMeta.js
110 lines (97 loc) · 2.74 KB
/
updateMeta.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import unionBy from 'lodash/unionBy'
export default {
// created will be called on both client and ssr
created () {
this.siteMeta = this.$site.headTags
.filter(([headerType]) => headerType === 'meta')
.map(([_, headerValue]) => headerValue)
if (this.$ssrContext) {
const mergedMetaItems = this.getMergedMetaTags()
this.$ssrContext.title = this.$title
this.$ssrContext.lang = this.$lang
this.$ssrContext.pageMeta = renderPageMeta(mergedMetaItems)
}
},
// Other life cycles will only be called at client
mounted () {
// init currentMetaTags from DOM
this.currentMetaTags = [...document.querySelectorAll('meta')]
// update title / meta tags
this.updateMeta()
},
methods: {
updateMeta () {
document.title = this.$title
document.documentElement.lang = this.$lang
const newMetaTags = this.getMergedMetaTags()
this.currentMetaTags = updateMetaTags(newMetaTags, this.currentMetaTags)
},
getMergedMetaTags () {
const pageMeta = this.$page.frontmatter.meta || []
// pageMetaTags have higher priority than siteMetaTags
// description needs special attention as it has too many entries
return unionBy([{ name: 'description', content: this.$description }],
pageMeta, this.siteMeta, metaIdentifier)
}
},
watch: {
$page () {
this.updateMeta()
}
},
beforeDestroy () {
updateMetaTags(null, this.currentMetaTags)
}
}
/**
* Replace currentMetaTags with newMetaTags
* @param {Array<Object>} newMetaTags
* @param {Array<HTMLElement>} currentMetaTags
* @returns {Array<HTMLElement>}
*/
function updateMetaTags (newMetaTags, currentMetaTags) {
if (currentMetaTags) {
[...currentMetaTags]
.filter(c => c.parentNode === document.head)
.forEach(c => document.head.removeChild(c))
}
if (newMetaTags) {
return newMetaTags.map(m => {
const tag = document.createElement('meta')
Object.keys(m).forEach(key => {
tag.setAttribute(key, m[key])
})
document.head.appendChild(tag)
return tag
})
}
}
/**
* Try to identify a meta tag by name, property or itemprop
*
* Return a complete string if none provided
* @param {Object} tag from frontmatter or siteMetaTags
* @returns {String}
*/
function metaIdentifier (tag) {
for (const item of ['name', 'property', 'itemprop']) {
if (tag.hasOwnProperty(item)) return tag[item] + item
}
return JSON.stringify(tag)
}
/**
* Render meta tags
*
* @param {Array} meta
* @returns {String}
*/
function renderPageMeta (meta) {
if (!meta) return ''
return meta.map(m => {
let res = `<meta`
Object.keys(m).forEach(key => {
res += ` ${key}="${m[key]}"`
})
return res + `>`
}).join('\n ')
}