diff --git a/src/client/updateClientMetaInfo.js b/src/client/updateClientMetaInfo.js index bc4906e..5978cfd 100644 --- a/src/client/updateClientMetaInfo.js +++ b/src/client/updateClientMetaInfo.js @@ -1,10 +1,23 @@ import updateTitle from './updaters/updateTitle' import updateTagAttributes from './updaters/updateTagAttributes' +import updateTags from './updaters/updateTags' import { SERVER_RENDERED_ATTRIBUTE } from '../shared/constants' +// tags to watch +const tags = [ + 'meta', + 'link', + 'base', + 'style', + 'script', + 'noscript' +] + +// hoisted vars but only in the browser if (typeof window !== 'undefined' && window !== null) { var htmlTag = document.getElementsByTagName('html')[0] var bodyTag = document.getElementsByTagName('body')[0] + var headTag = document.getElementsByTagName('head')[0] } /** @@ -23,6 +36,14 @@ export default function updateClientMetaInfo (newInfo, $root) { // update attrs updateTagAttributes(newInfo.bodyAttrs, bodyTag) + + // update tags + for (let i = 0, len = tags.length; i < len; i++) { + const tag = tags[i] + if (newInfo[tag]) { + updateTags(tag, newInfo[tag], headTag) + } + } } else { htmlTag.removeAttribute(SERVER_RENDERED_ATTRIBUTE) } diff --git a/src/client/updaters/updateTags.js b/src/client/updaters/updateTags.js new file mode 100644 index 0000000..3db1dbd --- /dev/null +++ b/src/client/updaters/updateTags.js @@ -0,0 +1,59 @@ +import { VUE_META_ATTRIBUTE } from '../../shared/constants' + +// borrow the slice method +const toArray = Function.prototype.call.bind(Array.prototype.slice) + +/** + * Updates meta tags inside on the client. Borrowed from `react-helmet`: + * https://github.com/nfl/react-helmet/blob/004d448f8de5f823d10f838b02317521180f34da/src/Helmet.js#L195-L245 + * + * @param {('meta'|'base'|'link'|'style'|'script'|'noscript')} type - the name of the tag + * @param {(Array|Object)} tags - an array of tag objects or a single object in case of base + * @return {Object} - a representation of what tags changed + */ +export default function updateTags (type, tags, headTag) { + const nodes = headTag.querySelectorAll(`${type}[${VUE_META_ATTRIBUTE}]`) + const oldTags = toArray(nodes) + const newTags = [] + let indexToDelete + + if (tags && tags.length) { + tags.forEach((tag) => { + const newElement = document.createElement(type) + + for (const attribute in tag) { + if (tag.hasOwnProperty(attribute)) { + if (attribute === 'innerHTML') { + newElement.innerHTML = tag.innerHTML + } else if (attribute === 'cssText') { + if (newElement.styleSheet) { + newElement.styleSheet.cssText = tag.cssText + } else { + newElement.appendChild(document.createTextNode(tag.cssText)) + } + } else { + const value = (typeof tag[attribute] === 'undefined') ? '' : tag[attribute] + newElement.setAttribute(attribute, value) + } + } + } + + newElement.setAttribute(VUE_META_ATTRIBUTE, 'true') + + // Remove a duplicate tag from domTagstoRemove, so it isn't cleared. + if (oldTags.some((existingTag, index) => { + indexToDelete = index + return newElement.isEqualNode(existingTag) + })) { + oldTags.splice(indexToDelete, 1) + } else { + newTags.push(newElement) + } + }) + } + + oldTags.forEach((tag) => tag.parentNode.removeChild(tag)) + newTags.forEach((tag) => headTag.appendChild(tag)) + + return { oldTags, newTags } +} diff --git a/src/server/generators/tagGenerator.js b/src/server/generators/tagGenerator.js index 6517e7a..4e6febd 100644 --- a/src/server/generators/tagGenerator.js +++ b/src/server/generators/tagGenerator.js @@ -10,11 +10,6 @@ import { VUE_META_ATTRIBUTE } from '../../shared/constants' export default function tagGenerator (type, tags) { return { text () { - // there can only be one `base` tag on a page, so we give it special treatment - if (type === 'base') { - tags = Object.keys(tags).length ? [tags] : [] - } - // build a string containing all tags of this type return tags.reduce((tagsStr, tag) => { // build a string containing all attributes of this tag diff --git a/src/server/inject.js b/src/server/inject.js index b46fc59..83b9263 100644 --- a/src/server/inject.js +++ b/src/server/inject.js @@ -21,7 +21,7 @@ export default function inject () { noscript: [], style: [], link: [], - base: {} + base: [] }, getMetaInfo(this.$root)) // generate server injectors diff --git a/src/shared/getMetaInfo.js b/src/shared/getMetaInfo.js index 46a6836..74ba49e 100644 --- a/src/shared/getMetaInfo.js +++ b/src/shared/getMetaInfo.js @@ -31,5 +31,10 @@ export default function getMetaInfo (component) { info.title = info.titleTemplate.replace(/%s/g, info.titleChunk) } + // convert base tag to an array + if (info.base) { + info.base = Object.keys(info.base).length ? [info.base] : [] + } + return info }