From 9eba2b558adf7fd8d3c94b2fbec017a72e02e93e Mon Sep 17 00:00:00 2001 From: pimlie Date: Fri, 13 Sep 2019 15:02:41 +0200 Subject: [PATCH] refactor: small bundle size improvements --- src/client/refresh.js | 13 +++--- src/client/update.js | 12 +++--- src/index.js | 3 +- src/server/inject.js | 9 +++-- src/shared/$meta.js | 16 ++++---- src/shared/additional-app.js | 14 +++---- src/shared/constants.js | 2 + src/shared/log.js | 2 + src/shared/meta-helpers.js | 5 ++- src/shared/mixin.js | 77 +++++++++++++++++++----------------- src/shared/nav-guards.js | 11 +++--- src/shared/pausing.js | 12 +++--- 12 files changed, 98 insertions(+), 78 deletions(-) diff --git a/src/client/refresh.js b/src/client/refresh.js index f4885ae..3d7793f 100644 --- a/src/client/refresh.js +++ b/src/client/refresh.js @@ -1,4 +1,5 @@ import { clientSequences } from '../shared/escaping' +import { rootConfigKey } from '../shared/constants' import { showWarningNotSupported } from '../shared/log' import { getComponentMetaInfo } from '../shared/getComponentOption' import { getAppsMetaInfo, clearAppsMetaInfo } from '../shared/additional-app' @@ -16,19 +17,19 @@ import updateClientMetaInfo from './updateClientMetaInfo' * * @return {Object} - new meta info */ -export default function refresh (vm, options = {}) { +export default function refresh (rootVm, options = {}) { // make sure vue-meta was initiated - if (!vm.$root._vueMeta) { + if (!rootVm[rootConfigKey]) { showWarningNotSupported() return {} } // collect & aggregate all metaInfo $options - const rawInfo = getComponentMetaInfo(options, vm.$root) + const rawInfo = getComponentMetaInfo(options, rootVm) - const metaInfo = getMetaInfo(options, rawInfo, clientSequences, vm.$root) + const metaInfo = getMetaInfo(options, rawInfo, clientSequences, rootVm) - const { appId } = vm.$root._vueMeta + const { appId } = rootVm[rootConfigKey] const tags = updateClientMetaInfo(appId, options, metaInfo) // emit "event" with new info @@ -45,5 +46,5 @@ export default function refresh (vm, options = {}) { clearAppsMetaInfo(true) } - return { vm, metaInfo, tags } + return { vm: rootVm, metaInfo, tags } } diff --git a/src/client/update.js b/src/client/update.js index a61eb78..5524472 100644 --- a/src/client/update.js +++ b/src/client/update.js @@ -1,17 +1,19 @@ +import { rootConfigKey } from '../shared/constants' + // store an id to keep track of DOM updates let batchId = null -export function triggerUpdate (vm, hookName) { +export function triggerUpdate (rootVm, hookName) { // if an update was triggered during initialization or when an update was triggered by the // metaInfo watcher, set initialized to null // then we keep falsy value but know we need to run a triggerUpdate after initialization - if (!vm.$root._vueMeta.initialized && (vm.$root._vueMeta.initializing || hookName === 'watcher')) { - vm.$root._vueMeta.initialized = null + if (!rootVm[rootConfigKey].initialized && (rootVm[rootConfigKey].initializing || hookName === 'watcher')) { + rootVm[rootConfigKey].initialized = null } - if (vm.$root._vueMeta.initialized && !vm.$root._vueMeta.paused) { + if (rootVm[rootConfigKey].initialized && !rootVm[rootConfigKey].paused) { // batch potential DOM updates to prevent extraneous re-rendering - batchUpdate(() => vm.$meta().refresh()) + batchUpdate(() => rootVm.$meta().refresh()) } } diff --git a/src/index.js b/src/index.js index 0ce148c..14c3393 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ import { version } from '../package.json' +import { showWarningNotSupportedInBrowserBundle } from './shared/log' import createMixin from './shared/mixin' import { setOptions } from './shared/options' import $meta from './shared/$meta' @@ -27,6 +28,6 @@ function install (Vue, options = {}) { export default { version, install, - generate: process.server ? generate : () => {}, + generate: process.server ? generate : () => showWarningNotSupportedInBrowserBundle('generate'), hasMetaInfo } diff --git a/src/server/inject.js b/src/server/inject.js index 49f00bd..022d0df 100644 --- a/src/server/inject.js +++ b/src/server/inject.js @@ -1,4 +1,5 @@ import { serverSequences } from '../shared/escaping' +import { rootConfigKey } from '../shared/constants' import { showWarningNotSupported } from '../shared/log' import { getComponentMetaInfo } from '../shared/getComponentOption' import { getAppsMetaInfo, clearAppsMetaInfo } from '../shared/additional-app' @@ -12,17 +13,17 @@ import generateServerInjector from './generateServerInjector' * @vm {Object} - Vue instance - ideally the root component * @return {Object} - server meta info with `toString` methods */ -export default function inject (vm, options = {}) { +export default function inject (rootVm, options = {}) { // make sure vue-meta was initiated - if (!vm.$root._vueMeta) { + if (!rootVm[rootConfigKey]) { showWarningNotSupported() return {} } // collect & aggregate all metaInfo $options - const rawInfo = getComponentMetaInfo(options, vm.$root) + const rawInfo = getComponentMetaInfo(options, rootVm) - const metaInfo = getMetaInfo(options, rawInfo, serverSequences, vm.$root) + const metaInfo = getMetaInfo(options, rawInfo, serverSequences, rootVm) // generate server injector const serverInjector = generateServerInjector(options, metaInfo) diff --git a/src/shared/$meta.js b/src/shared/$meta.js index 97ed969..fb816af 100644 --- a/src/shared/$meta.js +++ b/src/shared/$meta.js @@ -1,7 +1,7 @@ import refresh from '../client/refresh' import inject from '../server/inject' -import { showWarningNotSupported } from '../shared/log' import { addApp } from './additional-app' +import { showWarningNotSupportedInBrowserBundle } from './log' import { addNavGuards } from './nav-guards' import { pause, resume } from './pausing' import { getOptions } from './options' @@ -12,17 +12,19 @@ export default function $meta (options = {}) { * @this {Object} - the Vue instance (a root component) * @return {Object} - injector */ + const $root = this.$root + return { getOptions: () => getOptions(options), setOptions: ({ refreshOnceOnNavigation } = {}) => { if (refreshOnceOnNavigation) { - addNavGuards(this) + addNavGuards($root) } }, - refresh: () => refresh(this, options), - inject: () => process.server ? inject(this, options) : showWarningNotSupported(), - pause: () => pause(this), - resume: () => resume(this), - addApp: appId => addApp(this, appId, options) + refresh: () => refresh($root, options), + inject: () => process.server ? inject($root, options) : showWarningNotSupportedInBrowserBundle('inject'), + pause: () => pause($root), + resume: () => resume($root), + addApp: appId => addApp($root, appId, options) } } diff --git a/src/shared/additional-app.js b/src/shared/additional-app.js index ec58971..1035ff9 100644 --- a/src/shared/additional-app.js +++ b/src/shared/additional-app.js @@ -5,16 +5,16 @@ import { getTag, removeElementsByAppId } from '../utils/elements' let appsMetaInfo -export function addApp (vm, appId, options) { +export function addApp (rootVm, appId, options) { return { - set: metaInfo => setMetaInfo(vm.$root, appId, options, metaInfo), - remove: () => removeMetaInfo(vm.$root, appId, options) + set: metaInfo => setMetaInfo(rootVm, appId, options, metaInfo), + remove: () => removeMetaInfo(rootVm, appId, options) } } -export function setMetaInfo (vm, appId, options, metaInfo) { +export function setMetaInfo (rootVm, appId, options, metaInfo) { // if a vm exists _and_ its mounted then immediately update - if (vm && vm.$el) { + if (rootVm && rootVm.$el) { return updateClientMetaInfo(appId, options, metaInfo) } @@ -24,8 +24,8 @@ export function setMetaInfo (vm, appId, options, metaInfo) { appsMetaInfo[appId] = metaInfo } -export function removeMetaInfo (vm, appId, options) { - if (vm && vm.$el) { +export function removeMetaInfo (rootVm, appId, options) { + if (rootVm && rootVm.$el) { const tags = {} for (const type of metaInfoAttributeKeys) { const tagName = type.substr(0, 4) diff --git a/src/shared/constants.js b/src/shared/constants.js index 8e54002..a4327b1 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -20,6 +20,8 @@ export const defaultInfo = { __dangerouslyDisableSanitizersByTagID: {} } +export const rootConfigKey = '_vueMeta' + // 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. export const keyName = 'metaInfo' diff --git a/src/shared/log.js b/src/shared/log.js index 9955634..2983a7e 100644 --- a/src/shared/log.js +++ b/src/shared/log.js @@ -13,4 +13,6 @@ export function warn (str) { console.warn(str) } +export const showWarningNotSupportedInBrowserBundle = method => warn(`${method} is not supported in browser builds`) + export const showWarningNotSupported = () => warn('This vue app/component has no vue-meta configuration') diff --git a/src/shared/meta-helpers.js b/src/shared/meta-helpers.js index 347dd08..fa2982e 100644 --- a/src/shared/meta-helpers.js +++ b/src/shared/meta-helpers.js @@ -1,11 +1,12 @@ import { isUndefined, isObject } from '../utils/is-type' +import { rootConfigKey } from './constants' // Vue $root instance has a _vueMeta object property, otherwise its a boolean true export function hasMetaInfo (vm = this) { - return vm && (vm._vueMeta === true || isObject(vm._vueMeta)) + return vm && (vm[rootConfigKey] === true || isObject(vm[rootConfigKey])) } // a component is in a metaInfo branch when itself has meta info or one of its (grand-)children has export function inMetaInfoBranch (vm = this) { - return vm && !isUndefined(vm._vueMeta) + return vm && !isUndefined(vm[rootConfigKey]) } diff --git a/src/shared/mixin.js b/src/shared/mixin.js index 788e24a..2dc1648 100644 --- a/src/shared/mixin.js +++ b/src/shared/mixin.js @@ -1,6 +1,7 @@ import { triggerUpdate } from '../client/update' import { isUndefined, isFunction } from '../utils/is-type' import { ensuredPush } from '../utils/ensure' +import { rootConfigKey } from './constants' import { hasMetaInfo } from './meta-helpers' import { addNavGuards } from './nav-guards' import { warn } from './log' @@ -14,13 +15,17 @@ export default function createMixin (Vue, options) { // watch for client side component updates return { beforeCreate () { + const $root = this.$root + const $options = this.$options + const $isServer = this.$isServer + Object.defineProperty(this, '_hasMetaInfo', { configurable: true, get () { // Show deprecation warning once when devtools enabled - if (Vue.config.devtools && !this.$root._vueMeta.hasMetaInfoDeprecationWarningShown) { + if (Vue.config.devtools && !$root[rootConfigKey]._shown) { warn('VueMeta DeprecationWarning: _hasMetaInfo has been deprecated and will be removed in a future version. Please use hasMetaInfo(vm) instead') - this.$root._vueMeta.hasMetaInfoDeprecationWarningShown = true + $root[rootConfigKey]._shown = true } return hasMetaInfo(this) } @@ -29,24 +34,24 @@ export default function createMixin (Vue, options) { // Add a marker to know if it uses metaInfo // _vnode is used to know that it's attached to a real component // useful if we use some mixin to add some meta tags (like nuxt-i18n) - if (isUndefined(this.$options[options.keyName]) || this.$options[options.keyName] === null) { + if (isUndefined($options[options.keyName]) || $options[options.keyName] === null) { return } - if (!this.$root._vueMeta) { - this.$root._vueMeta = { appId } + if (!$root[rootConfigKey]) { + $root[rootConfigKey] = { appId } appId++ } // to speed up updates we keep track of branches which have a component with vue-meta info defined // if _vueMeta = true it has info, if _vueMeta = false a child has info - if (!this._vueMeta) { - this._vueMeta = true + if (!this[rootConfigKey]) { + this[rootConfigKey] = true let p = this.$parent - while (p && p !== this.$root) { - if (isUndefined(p._vueMeta)) { - p._vueMeta = false + while (p && p !== $root) { + if (isUndefined(p[rootConfigKey])) { + p[rootConfigKey] = false } p = p.$parent } @@ -54,19 +59,19 @@ export default function createMixin (Vue, options) { // coerce function-style metaInfo to a computed prop so we can observe // it on creation - if (isFunction(this.$options[options.keyName])) { - if (!this.$options.computed) { - this.$options.computed = {} + if (isFunction($options[options.keyName])) { + if (!$options.computed) { + $options.computed = {} } - this.$options.computed.$metaInfo = this.$options[options.keyName] + $options.computed.$metaInfo = $options[options.keyName] - if (!this.$isServer) { + if (!$isServer) { // if computed $metaInfo exists, watch it for updates & trigger a refresh // when it changes (i.e. automatically handle async actions that affect metaInfo) // credit for this suggestion goes to [Sébastien Chopin](https://github.com/Atinux) - ensuredPush(this.$options, 'created', () => { + ensuredPush($options, 'created', () => { this.$watch('$metaInfo', () => { - triggerUpdate(this, 'watcher') + triggerUpdate($root, 'watcher') }) }) } @@ -76,28 +81,28 @@ export default function createMixin (Vue, options) { // to triggerUpdate until this initial refresh is finished // this is to make sure that when a page is opened in an inactive tab which // has throttled rAF/timers we still immediately set the page title - if (isUndefined(this.$root._vueMeta.initialized)) { - this.$root._vueMeta.initialized = this.$isServer + if (isUndefined($root[rootConfigKey].initialized)) { + $root[rootConfigKey].initialized = $isServer - if (!this.$root._vueMeta.initialized) { - ensuredPush(this.$options, 'beforeMount', () => { + if (!$root[rootConfigKey].initialized) { + ensuredPush($options, 'beforeMount', () => { // if this Vue-app was server rendered, set the appId to 'ssr' // only one SSR app per page is supported - if (this.$root.$el && this.$root.$el.hasAttribute && this.$root.$el.hasAttribute('data-server-rendered')) { - this.$root._vueMeta.appId = options.ssrAppId + if ($root.$el && $root.$el.hasAttribute && $root.$el.hasAttribute('data-server-rendered')) { + $root[rootConfigKey].appId = options.ssrAppId } }) // we use the mounted hook here as on page load - ensuredPush(this.$options, 'mounted', () => { - if (!this.$root._vueMeta.initialized) { + ensuredPush($options, 'mounted', () => { + if (!$root[rootConfigKey].initialized) { // used in triggerUpdate to check if a change was triggered // during initialization - this.$root._vueMeta.initializing = true + $root[rootConfigKey].initializing = true // refresh meta in nextTick so all child components have loaded this.$nextTick(function () { - const { tags, metaInfo } = this.$root.$meta().refresh() + const { tags, metaInfo } = $root.$meta().refresh() // After ssr hydration (identifier by tags === false) check // if initialized was set to null in triggerUpdate. That'd mean @@ -105,17 +110,17 @@ export default function createMixin (Vue, options) { // to be applied OR a metaInfo watcher was triggered before the // current hook was called // (during initialization all changes are blocked) - if (tags === false && this.$root._vueMeta.initialized === null) { - this.$nextTick(() => triggerUpdate(this, 'initializing')) + if (tags === false && $root[rootConfigKey].initialized === null) { + this.$nextTick(() => triggerUpdate($root, 'initializing')) } - this.$root._vueMeta.initialized = true - delete this.$root._vueMeta.initializing + $root[rootConfigKey].initialized = true + delete $root[rootConfigKey].initializing // add the navigation guards if they havent been added yet // they are needed for the afterNavigation callback if (!options.refreshOnceOnNavigation && metaInfo.afterNavigation) { - addNavGuards(this) + addNavGuards($root) } }) } @@ -123,19 +128,19 @@ export default function createMixin (Vue, options) { // add the navigation guards if requested if (options.refreshOnceOnNavigation) { - addNavGuards(this) + addNavGuards($root) } } } // do not trigger refresh on the server side - if (this.$isServer) { + if ($isServer) { return } // no need to add this hooks on server side updateOnLifecycleHook.forEach((lifecycleHook) => { - ensuredPush(this.$options, lifecycleHook, () => triggerUpdate(this, lifecycleHook)) + ensuredPush($options, lifecycleHook, () => triggerUpdate(this, lifecycleHook)) }) }, // TODO: move back into beforeCreate when Vue issue is resolved @@ -155,7 +160,7 @@ export default function createMixin (Vue, options) { clearInterval(interval) - triggerUpdate(this, 'destroyed') + triggerUpdate(this.$root, 'destroyed') }, 50) } } diff --git a/src/shared/nav-guards.js b/src/shared/nav-guards.js index 106c414..6161d9d 100644 --- a/src/shared/nav-guards.js +++ b/src/shared/nav-guards.js @@ -1,16 +1,17 @@ import { isFunction } from '../utils/is-type' +import { rootConfigKey } from './constants' -export function addNavGuards (vm) { +export function addNavGuards (rootVm) { // return when nav guards already added or no router exists - if (vm.$root._vueMeta.navGuards || !vm.$root.$router) { + if (rootVm[rootConfigKey].navGuards || !rootVm.$router) { /* istanbul ignore next */ return } - vm.$root._vueMeta.navGuards = true + rootVm[rootConfigKey].navGuards = true - const $router = vm.$root.$router - const $meta = vm.$root.$meta() + const $router = rootVm.$router + const $meta = rootVm.$meta() $router.beforeEach((to, from, next) => { $meta.pause() diff --git a/src/shared/pausing.js b/src/shared/pausing.js index a2b0231..5d62d19 100644 --- a/src/shared/pausing.js +++ b/src/shared/pausing.js @@ -1,13 +1,15 @@ -export function pause (vm, refresh = true) { - vm.$root._vueMeta.paused = true +import { rootConfigKey } from './constants' + +export function pause (rootVm, refresh = true) { + rootVm[rootConfigKey].paused = true return () => resume(refresh) } -export function resume (vm, refresh = true) { - vm.$root._vueMeta.paused = false +export function resume (rootVm, refresh = true) { + rootVm[rootConfigKey].paused = false if (refresh) { - return vm.$root.$meta().refresh() + return rootVm.$meta().refresh() } }