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