-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
Copy pathupdateMeta.js
140 lines (121 loc) Β· 3.44 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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import unionBy from 'lodash/unionBy'
import escape from 'escape-html'
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)
this.$ssrContext.canonicalLink = renderCanonicalLink(this.$canonicalUrl)
}
},
// 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()
this.updateCanonicalLink()
},
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)
},
updateCanonicalLink () {
removeCanonicalLink()
if (!this.$canonicalUrl) {
return
}
document.head.insertAdjacentHTML('beforeend', renderCanonicalLink(this.$canonicalUrl))
}
},
watch: {
$page () {
this.updateMeta()
this.updateCanonicalLink()
}
},
beforeDestroy () {
updateMetaTags(null, this.currentMetaTags)
removeCanonicalLink()
}
}
function removeCanonicalLink () {
const canonicalEl = document.querySelector("link[rel='canonical']")
if (canonicalEl) {
canonicalEl.remove()
}
}
function renderCanonicalLink (link = '') {
if (!link) {
return ''
}
return `<link href="${link}" rel="canonical" />`
}
/**
* 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}="${escape(m[key])}"`
})
return res + `>`
}).join('\n ')
}