mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-24 20:41:50 +03:00
feat: add pause/resume methods to pause updates
This commit is contained in:
@@ -48,8 +48,8 @@ export default [{
|
||||
output: {
|
||||
...baseConfig.output,
|
||||
file: pkg.main,
|
||||
intro: 'var window',
|
||||
format: 'cjs'
|
||||
format: 'cjs',
|
||||
intro: 'var window'
|
||||
},
|
||||
external: Object.keys(pkg.dependencies)
|
||||
}]
|
||||
|
||||
+4
-1
@@ -1,3 +1,4 @@
|
||||
import { pause, resume } from '../shared/pausing'
|
||||
import refresh from './refresh'
|
||||
|
||||
export default function _$meta(options = {}) {
|
||||
@@ -9,7 +10,9 @@ export default function _$meta(options = {}) {
|
||||
return function $meta() {
|
||||
return {
|
||||
inject: () => {},
|
||||
refresh: refresh(options).bind(this)
|
||||
refresh: refresh(options).bind(this),
|
||||
pause: pause.bind(this),
|
||||
resume: resume.bind(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,10 @@ const startUpdate = (!isUndefined(window) ? window.requestAnimationFrame : null)
|
||||
* @return {Number} id - a new ID
|
||||
*/
|
||||
export default function batchUpdate(id, callback) {
|
||||
stopUpdate(id)
|
||||
if (id) {
|
||||
stopUpdate(id)
|
||||
}
|
||||
|
||||
return startUpdate(() => {
|
||||
id = null
|
||||
callback()
|
||||
|
||||
@@ -17,7 +17,6 @@ export default function _refresh(options = {}) {
|
||||
const metaInfo = getMetaInfo(options, this.$root)
|
||||
|
||||
const tags = updateClientMetaInfo(options, metaInfo)
|
||||
|
||||
// emit "event" with new info
|
||||
if (tags && isFunction(metaInfo.changed)) {
|
||||
metaInfo.changed.call(this, metaInfo, tags.addedTags, tags.removedTags)
|
||||
|
||||
+4
-1
@@ -1,4 +1,5 @@
|
||||
import refresh from '../client/refresh'
|
||||
import { pause, resume } from '../shared/pausing'
|
||||
import inject from './inject'
|
||||
|
||||
export default function _$meta(options = {}) {
|
||||
@@ -10,7 +11,9 @@ export default function _$meta(options = {}) {
|
||||
return function $meta() {
|
||||
return {
|
||||
inject: inject(options).bind(this),
|
||||
refresh: refresh(options).bind(this)
|
||||
refresh: refresh(options).bind(this),
|
||||
pause: pause.bind(this),
|
||||
resume: resume.bind(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-30
@@ -1,20 +1,11 @@
|
||||
import batchUpdate from '../client/batchUpdate'
|
||||
import triggerUpdate from '../client/triggerUpdate'
|
||||
import { isUndefined, isFunction } from '../shared/typeof'
|
||||
import { ensuredPush } from '../shared/ensure'
|
||||
|
||||
export default function createMixin(options) {
|
||||
// store an id to keep track of DOM updates
|
||||
let batchID = null
|
||||
|
||||
// for which Vue lifecycle hooks should the metaInfo be refreshed
|
||||
const updateOnLifecycleHook = ['activated', 'deactivated', 'beforeMount']
|
||||
|
||||
const triggerUpdate = (vm) => {
|
||||
if (vm.$root._vueMetaInitialized) {
|
||||
// batch potential DOM updates to prevent extraneous re-rendering
|
||||
batchID = batchUpdate(batchID, () => vm.$meta().refresh())
|
||||
}
|
||||
}
|
||||
|
||||
// watch for client side component updates
|
||||
return {
|
||||
beforeCreate() {
|
||||
@@ -36,41 +27,41 @@ export default function createMixin(options) {
|
||||
// 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)
|
||||
this.$options.created = this.$options.created || []
|
||||
this.$options.created.push(() => {
|
||||
this.$watch('$metaInfo', () => triggerUpdate(this))
|
||||
ensuredPush(this.$options, 'created', () => {
|
||||
this.$watch('$metaInfo', function () {
|
||||
triggerUpdate(this, 'watcher')
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
updateOnLifecycleHook.forEach((lifecycleHook) => {
|
||||
this.$options[lifecycleHook] = this.$options[lifecycleHook] || []
|
||||
this.$options[lifecycleHook].push(() => triggerUpdate(this))
|
||||
ensuredPush(this.$options, lifecycleHook, () => triggerUpdate(this, lifecycleHook))
|
||||
})
|
||||
|
||||
// force an initial refresh on page load and prevent other lifecycleHooks
|
||||
// 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 immeditately set the page title
|
||||
if (isUndefined(this.$root._vueMetaInitialized)) {
|
||||
this.$root._vueMetaInitialized = false
|
||||
if (isUndefined(this.$root._vueMetaPaused)) {
|
||||
this.$root._vueMetaInitialized = this.$isServer
|
||||
|
||||
this.$root.$options.mounted = this.$root.$options.mounted || []
|
||||
this.$root.$options.mounted.push(() => {
|
||||
if (!this.$root._vueMetaInitialized) {
|
||||
this.$nextTick(function () {
|
||||
this.$root.$meta().refresh()
|
||||
this.$root._vueMetaInitialized = true
|
||||
})
|
||||
}
|
||||
})
|
||||
if (!this.$root._vueMetaInitialized) {
|
||||
ensuredPush(this.$options, 'mounted', () => {
|
||||
if (!this.$root._vueMetaInitialized) {
|
||||
this.$nextTick(function () {
|
||||
this.$root.$meta().refresh()
|
||||
this.$root._vueMetaInitialized = true
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// do not trigger refresh on the server side
|
||||
if (!this.$isServer) {
|
||||
// re-render meta data when returning from a child component to parent
|
||||
this.$options.destroyed = this.$options.destroyed || []
|
||||
this.$options.destroyed.push(() => {
|
||||
ensuredPush(this.$options, 'destroyed', () => {
|
||||
// Wait that element is hidden before refreshing meta tags (to support animations)
|
||||
const interval = setInterval(() => {
|
||||
if (this.$el && this.$el.offsetParent !== null) {
|
||||
@@ -83,7 +74,7 @@ export default function createMixin(options) {
|
||||
return
|
||||
}
|
||||
|
||||
triggerUpdate(this)
|
||||
triggerUpdate(this, 'destroyed')
|
||||
}, 50)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { mount, defaultOptions, VueMetaBrowserPlugin, loadVueMetaPlugin } from './utils'
|
||||
import triggerUpdate from '../src/client/triggerUpdate'
|
||||
import batchUpdate from '../src/client/batchUpdate'
|
||||
import { mount, defaultOptions, vmTick, VueMetaBrowserPlugin, loadVueMetaPlugin } from './utils'
|
||||
|
||||
jest.mock('../src/client/triggerUpdate')
|
||||
jest.mock('../src/client/batchUpdate')
|
||||
jest.mock('../package.json', () => ({
|
||||
version: 'test-version'
|
||||
}))
|
||||
@@ -7,6 +11,7 @@ jest.mock('../package.json', () => ({
|
||||
describe('plugin', () => {
|
||||
let Vue
|
||||
|
||||
beforeEach(() => jest.clearAllMocks())
|
||||
beforeAll(() => (Vue = loadVueMetaPlugin(true)))
|
||||
|
||||
test('is loaded', () => {
|
||||
@@ -35,4 +40,64 @@ describe('plugin', () => {
|
||||
test('plugin sets package version', () => {
|
||||
expect(VueMetaBrowserPlugin.version).toBe('test-version')
|
||||
})
|
||||
|
||||
test('updates can be paused and resumed', async () => {
|
||||
const _triggerUpdate = jest.requireActual('../src/client/triggerUpdate').default
|
||||
const triggerUpdateSpy = triggerUpdate.mockImplementation(_triggerUpdate)
|
||||
|
||||
const Component = Vue.component('test-component', {
|
||||
metaInfo() {
|
||||
return {
|
||||
title: this.title
|
||||
}
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
template: '<div>Test</div>'
|
||||
})
|
||||
|
||||
let title = 'first title'
|
||||
const wrapper = mount(Component, {
|
||||
localVue: Vue,
|
||||
propsData: {
|
||||
title
|
||||
}
|
||||
})
|
||||
|
||||
// no batchUpdate on initialization
|
||||
expect(wrapper.vm.$root._vueMetaInitialized).toBe(false)
|
||||
expect(wrapper.vm.$root._vueMetaPaused).toBeFalsy()
|
||||
expect(triggerUpdateSpy).toHaveBeenCalledTimes(1)
|
||||
expect(batchUpdate).not.toHaveBeenCalled()
|
||||
jest.clearAllMocks()
|
||||
await vmTick(wrapper.vm)
|
||||
|
||||
title = 'second title'
|
||||
wrapper.setProps({ title })
|
||||
|
||||
// batchUpdate on normal update
|
||||
expect(wrapper.vm.$root._vueMetaInitialized).toBe(true)
|
||||
expect(wrapper.vm.$root._vueMetaPaused).toBeFalsy()
|
||||
expect(triggerUpdateSpy).toHaveBeenCalledTimes(1)
|
||||
expect(batchUpdate).toHaveBeenCalledTimes(1)
|
||||
jest.clearAllMocks()
|
||||
|
||||
wrapper.vm.$meta().pause()
|
||||
title = 'third title'
|
||||
wrapper.setProps({ title })
|
||||
|
||||
// no batchUpdate when paused
|
||||
expect(wrapper.vm.$root._vueMetaInitialized).toBe(true)
|
||||
expect(wrapper.vm.$root._vueMetaPaused).toBe(true)
|
||||
expect(triggerUpdateSpy).toHaveBeenCalledTimes(1)
|
||||
expect(batchUpdate).not.toHaveBeenCalled()
|
||||
jest.clearAllMocks()
|
||||
|
||||
const metaInfo = wrapper.vm.$meta().resume()
|
||||
expect(metaInfo.title).toBe(title)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -41,3 +41,9 @@ export function loadVueMetaPlugin(browser, options, localVue = getVue()) {
|
||||
|
||||
return localVue
|
||||
}
|
||||
|
||||
export const vmTick = (vm) => {
|
||||
return new Promise((resolve) => {
|
||||
vm.$nextTick(resolve)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user