mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-16 19:50:36 +03:00
feat: add support for setting attributes from multiple apps
chore: improve build size
This commit is contained in:
+5
-1
@@ -60,7 +60,11 @@ export function addListeners () {
|
||||
}
|
||||
|
||||
export function applyCallbacks (matchElement) {
|
||||
callbacks.forEach(([query, callback]) => {
|
||||
callbacks.forEach((args) => {
|
||||
// do not use destructuring for args, it increases transpiled size
|
||||
// due to var checks while we are guaranteed the structure of the cb
|
||||
const query = args[0]
|
||||
const callback = args[1]
|
||||
const selector = `${query}[onload="this.__vm_l=1"]`
|
||||
|
||||
let elements = []
|
||||
|
||||
@@ -56,7 +56,7 @@ export default function updateClientMetaInfo (appId, options = {}, newInfo) {
|
||||
|
||||
if (includes(metaInfoAttributeKeys, type)) {
|
||||
const tagName = type.substr(0, 4)
|
||||
updateAttribute(options, newInfo[type], getTag(tags, tagName))
|
||||
updateAttribute(appId, options, type, newInfo[type], getTag(tags, tagName))
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { booleanHtmlAttributes } from '../../shared/constants'
|
||||
import { toArray, includes } from '../../utils/array'
|
||||
import { isArray } from '../../utils/is-type'
|
||||
import { includes } from '../../utils/array'
|
||||
|
||||
// keep a local map of attribute values
|
||||
// instead of adding it to the html
|
||||
export const attributeMap = {}
|
||||
|
||||
/**
|
||||
* Updates the document's html tag attributes
|
||||
@@ -8,39 +11,62 @@ import { isArray } from '../../utils/is-type'
|
||||
* @param {Object} attrs - the new document html attributes
|
||||
* @param {HTMLElement} tag - the HTMLElement tag to update with new attrs
|
||||
*/
|
||||
export default function updateAttribute ({ attribute } = {}, attrs, tag) {
|
||||
export default function updateAttribute (appId, { attribute } = {}, type, attrs, tag) {
|
||||
const vueMetaAttrString = tag.getAttribute(attribute)
|
||||
const vueMetaAttrs = vueMetaAttrString ? vueMetaAttrString.split(',') : []
|
||||
const toRemove = toArray(vueMetaAttrs)
|
||||
if (vueMetaAttrString) {
|
||||
attributeMap[type] = JSON.parse(decodeURI(vueMetaAttrString))
|
||||
tag.removeAttribute(attribute)
|
||||
}
|
||||
|
||||
const keepIndexes = []
|
||||
for (const attr in attrs) {
|
||||
if (attrs.hasOwnProperty(attr)) {
|
||||
const value = includes(booleanHtmlAttributes, attr)
|
||||
? ''
|
||||
: isArray(attrs[attr]) ? attrs[attr].join(' ') : attrs[attr]
|
||||
let data = attributeMap[type] || {}
|
||||
|
||||
tag.setAttribute(attr, value || '')
|
||||
const toUpdate = []
|
||||
|
||||
if (!includes(vueMetaAttrs, attr)) {
|
||||
vueMetaAttrs.push(attr)
|
||||
// remove attributes from the map
|
||||
// which have been removed for this appId
|
||||
for (const attr in data) {
|
||||
if (data[attr] && appId in data[attr]) {
|
||||
toUpdate.push(attr)
|
||||
|
||||
if (!attrs[attr]) {
|
||||
delete data[attr][appId]
|
||||
}
|
||||
|
||||
// filter below wont ever check -1
|
||||
keepIndexes.push(toRemove.indexOf(attr))
|
||||
}
|
||||
}
|
||||
|
||||
const removedAttributesCount = toRemove
|
||||
.filter((el, index) => !includes(keepIndexes, index))
|
||||
.reduce((acc, attr) => {
|
||||
tag.removeAttribute(attr)
|
||||
return acc + 1
|
||||
}, 0)
|
||||
for (const attr in attrs) {
|
||||
const attrData = data[attr]
|
||||
|
||||
if (vueMetaAttrs.length === removedAttributesCount) {
|
||||
tag.removeAttribute(attribute)
|
||||
} else {
|
||||
tag.setAttribute(attribute, (vueMetaAttrs.sort()).join(','))
|
||||
if (!attrData || attrData[appId] !== attrs[attr]) {
|
||||
toUpdate.push(attr)
|
||||
|
||||
if (attrs[attr]) {
|
||||
data[attr] = data[attr] || {}
|
||||
data[attr][appId] = attrs[attr]
|
||||
} else {
|
||||
delete data[attr][appId]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const attr of toUpdate) {
|
||||
const attrData = data[attr]
|
||||
|
||||
const attrValues = []
|
||||
for (const appId in attrData) {
|
||||
Array.prototype.push.apply(attrValues, [].concat(attrData[appId]))
|
||||
}
|
||||
|
||||
if (attrValues.length) {
|
||||
const attrValue = includes(booleanHtmlAttributes, attr) && attrValues.some(Boolean)
|
||||
? ''
|
||||
: attrValues.filter(Boolean).join(' ')
|
||||
|
||||
tag.setAttribute(attr, attrValue)
|
||||
} else {
|
||||
tag.removeAttribute(attr)
|
||||
}
|
||||
}
|
||||
|
||||
attributeMap[type] = data
|
||||
}
|
||||
|
||||
@@ -47,17 +47,30 @@ export default function generateServerInjector (options, metaInfo) {
|
||||
}
|
||||
|
||||
if (metaInfoAttributeKeys.includes(type)) {
|
||||
let str = attributeGenerator(options, type, serverInjector.data[type], arg)
|
||||
const attributeData = {}
|
||||
|
||||
if (serverInjector.extraData) {
|
||||
for (const appId in serverInjector.extraData) {
|
||||
const data = serverInjector.extraData[appId][type]
|
||||
const extraStr = attributeGenerator(options, type, data, arg)
|
||||
str = `${str}${extraStr}`
|
||||
const data = serverInjector.data[type]
|
||||
if (data) {
|
||||
for (const attr in data) {
|
||||
attributeData[attr] = {
|
||||
[options.ssrAppId]: data[attr]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
for (const appId in serverInjector.extraData) {
|
||||
const data = serverInjector.extraData[appId][type]
|
||||
if (data) {
|
||||
for (const attr in data) {
|
||||
attributeData[attr] = {
|
||||
...attributeData[attr],
|
||||
[appId]: data[attr]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return attributeGenerator(options, type, attributeData, arg)
|
||||
}
|
||||
|
||||
let str = tagGenerator(options, type, serverInjector.data[type], arg)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { booleanHtmlAttributes } from '../../shared/constants'
|
||||
import { isUndefined, isArray } from '../../utils/is-type'
|
||||
|
||||
/**
|
||||
* Generates tag attributes for use on the server.
|
||||
@@ -10,22 +9,26 @@ import { isUndefined, isArray } from '../../utils/is-type'
|
||||
*/
|
||||
export default function attributeGenerator ({ attribute, ssrAttribute } = {}, type, data, addSrrAttribute) {
|
||||
let attributeStr = ''
|
||||
const watchedAttrs = []
|
||||
|
||||
for (const attr in data) {
|
||||
if (data.hasOwnProperty(attr)) {
|
||||
watchedAttrs.push(attr)
|
||||
const attrData = data[attr]
|
||||
const attrValues = []
|
||||
|
||||
attributeStr += isUndefined(data[attr]) || booleanHtmlAttributes.includes(attr)
|
||||
? attr
|
||||
: `${attr}="${isArray(data[attr]) ? data[attr].join(' ') : data[attr]}"`
|
||||
for (const appId in attrData) {
|
||||
attrValues.push(...[].concat(attrData[appId]))
|
||||
}
|
||||
|
||||
if (attrValues.length) {
|
||||
attributeStr += booleanHtmlAttributes.includes(attr) && attrValues.some(Boolean)
|
||||
? `${attr}`
|
||||
: `${attr}="${attrValues.join(' ')}"`
|
||||
|
||||
attributeStr += ' '
|
||||
}
|
||||
}
|
||||
|
||||
if (attributeStr) {
|
||||
attributeStr += `${attribute}="${(watchedAttrs.sort()).join(',')}"`
|
||||
attributeStr += `${attribute}="${encodeURI(JSON.stringify(data))}"`
|
||||
}
|
||||
|
||||
if (type === 'htmlAttrs' && addSrrAttribute) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import updateClientMetaInfo from '../client/updateClientMetaInfo'
|
||||
import { removeElementsByAppId } from '../utils/elements'
|
||||
import { updateAttribute } from '../client/updaters'
|
||||
import { metaInfoAttributeKeys } from '../shared/constants'
|
||||
import { getTag, removeElementsByAppId } from '../utils/elements'
|
||||
|
||||
let appsMetaInfo
|
||||
|
||||
@@ -24,6 +26,12 @@ export function setMetaInfo (vm, appId, options, metaInfo) {
|
||||
|
||||
export function removeMetaInfo (vm, appId, options) {
|
||||
if (vm && vm.$el) {
|
||||
const tags = {}
|
||||
for (const type of metaInfoAttributeKeys) {
|
||||
const tagName = type.substr(0, 4)
|
||||
updateAttribute(appId, options, type, {}, getTag(tags, tagName))
|
||||
}
|
||||
|
||||
return removeElementsByAppId(options, appId)
|
||||
}
|
||||
|
||||
|
||||
@@ -57,19 +57,18 @@ export const defaultOptions = {
|
||||
ssrAppId
|
||||
}
|
||||
|
||||
// The metaInfo property keys which are used to disable escaping
|
||||
export const disableOptionKeys = [
|
||||
'__dangerouslyDisableSanitizers',
|
||||
'__dangerouslyDisableSanitizersByTagID'
|
||||
]
|
||||
|
||||
// List of metaInfo property keys which are configuration options (and dont generate html)
|
||||
export const metaInfoOptionKeys = [
|
||||
'titleChunk',
|
||||
'titleTemplate',
|
||||
'changed',
|
||||
'__dangerouslyDisableSanitizers',
|
||||
'__dangerouslyDisableSanitizersByTagID'
|
||||
]
|
||||
|
||||
// The metaInfo property keys which are used to disable escaping
|
||||
export const disableOptionKeys = [
|
||||
'__dangerouslyDisableSanitizers',
|
||||
'__dangerouslyDisableSanitizersByTagID'
|
||||
...disableOptionKeys
|
||||
]
|
||||
|
||||
// List of metaInfo property keys which only generates attributes and no tags
|
||||
|
||||
@@ -34,7 +34,10 @@ export function escape (info, options, escapeOptions, escapeKeys) {
|
||||
continue
|
||||
}
|
||||
|
||||
let [ disableKey ] = disableOptionKeys
|
||||
// do not use destructuring for disableOptionKeys, it increases transpiled size
|
||||
// due to var checks while we are guaranteed the structure of the cb
|
||||
let disableKey = disableOptionKeys[0]
|
||||
|
||||
if (escapeOptions[disableKey] && includes(escapeOptions[disableKey], key)) {
|
||||
// this info[key] doesnt need to escaped if the option is listed in __dangerouslyDisableSanitizers
|
||||
escaped[key] = value
|
||||
@@ -81,8 +84,10 @@ export function escape (info, options, escapeOptions, escapeKeys) {
|
||||
}
|
||||
|
||||
export function escapeMetaInfo (options, info, escapeSequences = []) {
|
||||
// do not use destructuring for seq, it increases transpiled size
|
||||
// due to var checks while we are guaranteed the structure of the cb
|
||||
const escapeOptions = {
|
||||
doEscape: value => escapeSequences.reduce((val, [v, r]) => val.replace(v, r), value)
|
||||
doEscape: value => escapeSequences.reduce((val, seq) => val.replace(seq[0], seq[1]), value)
|
||||
}
|
||||
|
||||
disableOptionKeys.forEach((disableKey, index) => {
|
||||
|
||||
Reference in New Issue
Block a user