Skip to content

Commit

Permalink
refactor: small bundle size improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
pimlie committed Sep 17, 2019
1 parent 398d1af commit 9eba2b5
Show file tree
Hide file tree
Showing 12 changed files with 98 additions and 78 deletions.
13 changes: 7 additions & 6 deletions src/client/refresh.js
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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
Expand All @@ -45,5 +46,5 @@ export default function refresh (vm, options = {}) {
clearAppsMetaInfo(true)
}

return { vm, metaInfo, tags }
return { vm: rootVm, metaInfo, tags }
}
12 changes: 7 additions & 5 deletions src/client/update.js
Original file line number Diff line number Diff line change
@@ -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())
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -27,6 +28,6 @@ function install (Vue, options = {}) {
export default {
version,
install,
generate: process.server ? generate : () => {},
generate: process.server ? generate : () => showWarningNotSupportedInBrowserBundle('generate'),
hasMetaInfo
}
9 changes: 5 additions & 4 deletions src/server/inject.js
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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)
Expand Down
16 changes: 9 additions & 7 deletions src/shared/$meta.js
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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)
}
}
14 changes: 7 additions & 7 deletions src/shared/additional-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/shared/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 2 additions & 0 deletions src/shared/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
5 changes: 3 additions & 2 deletions src/shared/meta-helpers.js
Original file line number Diff line number Diff line change
@@ -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])
}
77 changes: 41 additions & 36 deletions src/shared/mixin.js
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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)
}
Expand All @@ -29,44 +34,44 @@ 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
}
}

// 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')
})
})
}
Expand All @@ -76,66 +81,66 @@ 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
// that during initilazation changes where triggered which need
// 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)
}
})
}
})

// 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
Expand All @@ -155,7 +160,7 @@ export default function createMixin (Vue, options) {

clearInterval(interval)

triggerUpdate(this, 'destroyed')
triggerUpdate(this.$root, 'destroyed')
}, 50)
}
}
Expand Down

0 comments on commit 9eba2b5

Please sign in to comment.