-
Notifications
You must be signed in to change notification settings - Fork 4.7k
fix: duplicate meta tags #2164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
fix: duplicate meta tags #2164
Changes from 4 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
5a02e2a
fix: duplicate meta in ssr
147d619
fix: page metas have higher priority
4f50deb
Revert "fix: duplicate meta in ssr"
f6ca397
fix: render meta tags during ssr
0f91886
improve readability from suggestions
1e77cf3
fix: missing spaces
257f0ab
refactor: remove unnecessary code
a853352
fix: siteMetaTags aren't correctly init
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,38 +1,53 @@ | ||||||
import unionBy from 'lodash/unionBy' | ||||||
|
||||||
export default { | ||||||
// created will be called on both client and ssr | ||||||
created () { | ||||||
if (this.$ssrContext) { | ||||||
const siteMetaTags = this.$site.headTags | ||||||
.filter(item => item[0] === 'meta') | ||||||
.map(item => item[1]) | ||||||
|
||||||
const meta = this.getMergedMetaTags(siteMetaTags) | ||||||
haoranpb marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
this.$ssrContext.title = this.$title | ||||||
this.$ssrContext.lang = this.$lang | ||||||
this.$ssrContext.description = this.$page.description || this.$description | ||||||
this.$ssrContext.pageMeta = renderPageMeta(meta) | ||||||
haoranpb marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} | ||||||
}, | ||||||
|
||||||
// Other life cycles will only be called at client | ||||||
mounted () { | ||||||
// init currentMetaTags from DOM | ||||||
this.currentMetaTags = [...document.querySelectorAll('meta')] | ||||||
|
||||||
// indirectly init siteMetaTags from DOM | ||||||
this.siteMetaTags = this.currentMetaTags.map(element => { | ||||||
const siteMeta = {} | ||||||
for (const attribute of element.attributes) { | ||||||
siteMeta[attribute.name] = attribute.value | ||||||
} | ||||||
return siteMeta | ||||||
}) | ||||||
haoranpb marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
// update title / meta tags | ||||||
this.currentMetaTags = new Set() | ||||||
this.updateMeta() | ||||||
}, | ||||||
|
||||||
methods: { | ||||||
updateMeta () { | ||||||
document.title = this.$title | ||||||
document.documentElement.lang = this.$lang | ||||||
const userMeta = this.$page.frontmatter.meta || [] | ||||||
const meta = userMeta.slice(0) | ||||||
const useGlobalDescription = userMeta.filter(m => m.name === 'description').length === 0 | ||||||
|
||||||
// #665 Avoid duplicate description meta at runtime. | ||||||
if (useGlobalDescription) { | ||||||
meta.push({ name: 'description', content: this.$description }) | ||||||
} | ||||||
|
||||||
// Including description meta coming from SSR. | ||||||
const descriptionMetas = document.querySelectorAll('meta[name="description"]') | ||||||
if (descriptionMetas.length) { | ||||||
descriptionMetas.forEach(m => this.currentMetaTags.add(m)) | ||||||
} | ||||||
const newMetaTags = this.getMergedMetaTags(this.siteMetaTags) | ||||||
this.currentMetaTags = updateMetaTags(newMetaTags, this.currentMetaTags) | ||||||
}, | ||||||
|
||||||
this.currentMetaTags = new Set(updateMetaTags(meta, this.currentMetaTags)) | ||||||
getMergedMetaTags (siteMeta) { | ||||||
const pageMeta = (this.$page.frontmatter.meta || []).slice(0) | ||||||
haoranpb marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
// pageMetaTags have higher priority than siteMetaTags | ||||||
// description needs special attention for it has too many entries | ||||||
haoranpb marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
return unionBy([{ name: 'description', content: this.$description }], | ||||||
pageMeta, siteMeta, metaIdentifier) | ||||||
} | ||||||
}, | ||||||
|
||||||
|
@@ -47,14 +62,20 @@ export default { | |||||
} | ||||||
} | ||||||
|
||||||
function updateMetaTags (meta, current) { | ||||||
if (current) { | ||||||
[...current].forEach(c => { | ||||||
/** | ||||||
* Replace currentMetaTags with newMetaTags | ||||||
* @param {Array<Object>} newMetaTags | ||||||
* @param {Array<HTMLElement>} currentMetaTags | ||||||
* @returns {Array<HTMLElement>} | ||||||
*/ | ||||||
function updateMetaTags (newMetaTags, currentMetaTags) { | ||||||
if (currentMetaTags) { | ||||||
[...currentMetaTags].forEach(c => { | ||||||
document.head.removeChild(c) | ||||||
}) | ||||||
} | ||||||
if (meta) { | ||||||
return meta.map(m => { | ||||||
if (newMetaTags) { | ||||||
return newMetaTags.map(m => { | ||||||
const tag = document.createElement('meta') | ||||||
Object.keys(m).forEach(key => { | ||||||
tag.setAttribute(key, m[key]) | ||||||
|
@@ -64,3 +85,35 @@ function updateMetaTags (meta, current) { | |||||
}) | ||||||
} | ||||||
} | ||||||
|
||||||
/** | ||||||
* 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 {Array<string>} | ||||||
*/ | ||||||
|
||||||
function renderPageMeta (meta) { | ||||||
if (!meta) return '' | ||||||
return meta.map(m => { | ||||||
let res = `<meta` | ||||||
Object.keys(m).forEach(key => { | ||||||
res += ` ${key}="${m[key]}"` | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be escaped? For example, this module.exports = {
description: 'Image cache & resize service',
} renders as (within version 1.4.1): <meta name="description" content="Image cache & resize service"> which is probably not correct. fwiw, version 1.4.0 didn't had this problem. Suggestion:
Suggested change
and this import at the top of the file: const escape = require('escape-html') |
||||||
}) | ||||||
return res + `>` | ||||||
}).join('\n ') | ||||||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.