2
0
mirror of https://github.com/tenrok/vue-meta.git synced 2026-06-25 05:30:33 +03:00

refactor: optimize getMetaInfo by extracting functions

this should make it easier for javascript engines to optimize these functions
This commit is contained in:
pimlie
2019-03-08 13:33:24 +01:00
committed by Alexander Lichter
parent 12c7949132
commit 7b888c9437
2 changed files with 43 additions and 106 deletions
+24
View File
@@ -2,6 +2,24 @@
* These are constant variables used throughout the application. * These are constant variables used throughout the application.
*/ */
// set some sane defaults
export const defaultInfo = {
title: '',
titleChunk: '',
titleTemplate: '%s',
htmlAttrs: {},
bodyAttrs: {},
headAttrs: {},
base: [],
link: [],
meta: [],
style: [],
script: [],
noscript: [],
__dangerouslyDisableSanitizers: [],
__dangerouslyDisableSanitizersByTagID: {}
}
// This is the name of the component option that contains all the information that // This is the name of the component option that contains all the information that
// gets converted to the various meta tags & attributes for the page. // gets converted to the various meta tags & attributes for the page.
export const keyName = 'metaInfo' export const keyName = 'metaInfo'
@@ -35,6 +53,12 @@ export const metaInfoOptionKeys = [
'__dangerouslyDisableSanitizersByTagID' '__dangerouslyDisableSanitizersByTagID'
] ]
// The metaInfo property keys which are used to disable escaping
export const disableOptionKeys = [
'__dangerouslyDisableSanitizers',
'__dangerouslyDisableSanitizersByTagID'
]
// List of metaInfo property keys which only generates attributes and no tags // List of metaInfo property keys which only generates attributes and no tags
export const metaInfoAttributeKeys = [ export const metaInfoAttributeKeys = [
'htmlAttrs', 'htmlAttrs',
+19 -106
View File
@@ -1,12 +1,9 @@
import deepmerge from 'deepmerge' import applyTemplate from './applyTemplate'
import isPlainObject from 'lodash.isplainobject' import { defaultInfo, disableOptionKeys } from './constants'
import { isFunction, isString } from './typeof' import { ensureIsArray } from './ensure'
import isArray from './isArray' import escape from './escape'
import getComponentOption from './getComponentOption' import getComponentOption from './getComponentOption'
const applyTemplate = (component, template, chunk) =>
isFunction(template) ? template.call(component, chunk) : template.replace(/%s/g, chunk)
/** /**
* Returns the correct meta info for the given component * Returns the correct meta info for the given component
* (child components will overwrite parent meta info) * (child components will overwrite parent meta info)
@@ -15,74 +12,14 @@ const applyTemplate = (component, template, chunk) =>
* @return {Object} - returned meta info * @return {Object} - returned meta info
*/ */
export default function getMetaInfo({ keyName, tagIDKeyName, metaTemplateKeyName, contentKeyName } = {}, component, escapeSequences = []) { export default function getMetaInfo({ keyName, tagIDKeyName, metaTemplateKeyName, contentKeyName } = {}, component, escapeSequences = []) {
// set some sane defaults
const defaultInfo = {
title: '',
titleChunk: '',
titleTemplate: '%s',
htmlAttrs: {},
bodyAttrs: {},
headAttrs: {},
meta: [],
base: [],
link: [],
style: [],
script: [],
noscript: [],
__dangerouslyDisableSanitizers: [],
__dangerouslyDisableSanitizersByTagID: {}
}
// collect & aggregate all metaInfo $options // collect & aggregate all metaInfo $options
let info = getComponentOption({ let info = getComponentOption({
deep: true,
component, component,
keyName, keyName,
metaTemplateKeyName, metaTemplateKeyName,
tagIDKeyName, tagIDKeyName,
contentKeyName, contentKeyName
arrayMerge(target, source) { }, defaultInfo)
// we concat the arrays without merging objects contained in,
// but we check for a `vmid` property on each object in the array
// using an O(1) lookup associative array exploit
// note the use of "for in" - we are looping through arrays here, not
// plain objects
const destination = []
for (const targetIndex in target) {
const targetItem = target[targetIndex]
let shared = false
for (const sourceIndex in source) {
const sourceItem = source[sourceIndex]
if (targetItem[tagIDKeyName] && targetItem[tagIDKeyName] === sourceItem[tagIDKeyName]) {
const targetTemplate = targetItem[metaTemplateKeyName]
const sourceTemplate = sourceItem[metaTemplateKeyName]
if (targetTemplate && !sourceTemplate) {
sourceItem[contentKeyName] = applyTemplate(component, targetTemplate, sourceItem[contentKeyName])
}
// If template defined in child but content in parent
if (targetTemplate && sourceTemplate && !sourceItem[contentKeyName]) {
sourceItem[contentKeyName] = applyTemplate(component, sourceTemplate, targetItem[contentKeyName])
delete sourceItem[metaTemplateKeyName]
}
shared = true
break
}
}
if (!shared) {
destination.push(targetItem)
}
}
return destination.concat(source)
}
})
// Remove all "template" tags from meta // Remove all "template" tags from meta
@@ -92,8 +29,8 @@ export default function getMetaInfo({ keyName, tagIDKeyName, metaTemplateKeyName
} }
// replace title with populated template // replace title with populated template
if (info.titleTemplate) { if (info.titleTemplate && info.titleTemplate !== '%s') {
info.title = applyTemplate(component, info.titleTemplate, info.titleChunk || '') applyTemplate({ component, contentKeyName: 'title' }, info, info.titleTemplate, info.titleChunk || '')
} }
// convert base tag to an array so it can be handled the same way // convert base tag to an array so it can be handled the same way
@@ -102,47 +39,23 @@ export default function getMetaInfo({ keyName, tagIDKeyName, metaTemplateKeyName
info.base = Object.keys(info.base).length ? [info.base] : [] info.base = Object.keys(info.base).length ? [info.base] : []
} }
const ref = info.__dangerouslyDisableSanitizers for (const index in disableOptionKeys) {
const refByTagID = info.__dangerouslyDisableSanitizersByTagID const disableKey = disableOptionKeys[index]
if (!info[disableKey]) {
// sanitizes potentially dangerous characters continue
const escape = info => Object.keys(info).reduce((escaped, key) => {
let isDisabled = ref && ref.includes(key)
const tagID = info[tagIDKeyName]
if (!isDisabled && tagID) {
isDisabled = refByTagID && refByTagID[tagID] && refByTagID[tagID].includes(key)
} }
const val = info[key] if (index === 0) {
escaped[key] = val ensureIsArray(info, disableKey)
} else if (index === 1) {
if (key === '__dangerouslyDisableSanitizers' || key === '__dangerouslyDisableSanitizersByTagID') { for (const key in info[disableKey]) {
return escaped ensureIsArray(info[disableKey], key)
}
if (!isDisabled) {
if (isString(val)) {
escaped[key] = escapeSequences.reduce((val, [v, r]) => val.replace(v, r), val)
} else if (isPlainObject(val)) {
escaped[key] = escape(val)
} else if (isArray(val)) {
escaped[key] = val.map(escape)
} else {
escaped[key] = val
} }
} else {
escaped[key] = val
} }
}
return escaped
}, {})
// merge with defaults
info = deepmerge(defaultInfo, info)
// begin sanitization // begin sanitization
info = escape(info) info = escape(info, { tagIDKeyName }, escapeSequences)
return info return info
} }