mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-22 18:30:35 +03:00
feat: add pause/resume methods to pause updates
This commit is contained in:
@@ -48,8 +48,8 @@ export default [{
|
|||||||
output: {
|
output: {
|
||||||
...baseConfig.output,
|
...baseConfig.output,
|
||||||
file: pkg.main,
|
file: pkg.main,
|
||||||
intro: 'var window',
|
format: 'cjs',
|
||||||
format: 'cjs'
|
intro: 'var window'
|
||||||
},
|
},
|
||||||
external: Object.keys(pkg.dependencies)
|
external: Object.keys(pkg.dependencies)
|
||||||
}]
|
}]
|
||||||
|
|||||||
+4
-1
@@ -1,3 +1,4 @@
|
|||||||
|
import { pause, resume } from '../shared/pausing'
|
||||||
import refresh from './refresh'
|
import refresh from './refresh'
|
||||||
|
|
||||||
export default function _$meta(options = {}) {
|
export default function _$meta(options = {}) {
|
||||||
@@ -9,7 +10,9 @@ export default function _$meta(options = {}) {
|
|||||||
return function $meta() {
|
return function $meta() {
|
||||||
return {
|
return {
|
||||||
inject: () => {},
|
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
|
* @return {Number} id - a new ID
|
||||||
*/
|
*/
|
||||||
export default function batchUpdate(id, callback) {
|
export default function batchUpdate(id, callback) {
|
||||||
stopUpdate(id)
|
if (id) {
|
||||||
|
stopUpdate(id)
|
||||||
|
}
|
||||||
|
|
||||||
return startUpdate(() => {
|
return startUpdate(() => {
|
||||||
id = null
|
id = null
|
||||||
callback()
|
callback()
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export default function _refresh(options = {}) {
|
|||||||
const metaInfo = getMetaInfo(options, this.$root)
|
const metaInfo = getMetaInfo(options, this.$root)
|
||||||
|
|
||||||
const tags = updateClientMetaInfo(options, metaInfo)
|
const tags = updateClientMetaInfo(options, metaInfo)
|
||||||
|
|
||||||
// emit "event" with new info
|
// emit "event" with new info
|
||||||
if (tags && isFunction(metaInfo.changed)) {
|
if (tags && isFunction(metaInfo.changed)) {
|
||||||
metaInfo.changed.call(this, metaInfo, tags.addedTags, tags.removedTags)
|
metaInfo.changed.call(this, metaInfo, tags.addedTags, tags.removedTags)
|
||||||
|
|||||||
+4
-1
@@ -1,4 +1,5 @@
|
|||||||
import refresh from '../client/refresh'
|
import refresh from '../client/refresh'
|
||||||
|
import { pause, resume } from '../shared/pausing'
|
||||||
import inject from './inject'
|
import inject from './inject'
|
||||||
|
|
||||||
export default function _$meta(options = {}) {
|
export default function _$meta(options = {}) {
|
||||||
@@ -10,7 +11,9 @@ export default function _$meta(options = {}) {
|
|||||||
return function $meta() {
|
return function $meta() {
|
||||||
return {
|
return {
|
||||||
inject: inject(options).bind(this),
|
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 { isUndefined, isFunction } from '../shared/typeof'
|
||||||
|
import { ensuredPush } from '../shared/ensure'
|
||||||
|
|
||||||
export default function createMixin(options) {
|
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
|
// for which Vue lifecycle hooks should the metaInfo be refreshed
|
||||||
const updateOnLifecycleHook = ['activated', 'deactivated', 'beforeMount']
|
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
|
// watch for client side component updates
|
||||||
return {
|
return {
|
||||||
beforeCreate() {
|
beforeCreate() {
|
||||||
@@ -36,41 +27,41 @@ export default function createMixin(options) {
|
|||||||
// if computed $metaInfo exists, watch it for updates & trigger a refresh
|
// if computed $metaInfo exists, watch it for updates & trigger a refresh
|
||||||
// when it changes (i.e. automatically handle async actions that affect metaInfo)
|
// 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)
|
// credit for this suggestion goes to [Sébastien Chopin](https://github.com/Atinux)
|
||||||
this.$options.created = this.$options.created || []
|
ensuredPush(this.$options, 'created', () => {
|
||||||
this.$options.created.push(() => {
|
this.$watch('$metaInfo', function () {
|
||||||
this.$watch('$metaInfo', () => triggerUpdate(this))
|
triggerUpdate(this, 'watcher')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOnLifecycleHook.forEach((lifecycleHook) => {
|
updateOnLifecycleHook.forEach((lifecycleHook) => {
|
||||||
this.$options[lifecycleHook] = this.$options[lifecycleHook] || []
|
ensuredPush(this.$options, lifecycleHook, () => triggerUpdate(this, lifecycleHook))
|
||||||
this.$options[lifecycleHook].push(() => triggerUpdate(this))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// force an initial refresh on page load and prevent other lifecycleHooks
|
// force an initial refresh on page load and prevent other lifecycleHooks
|
||||||
// to triggerUpdate until this initial refresh is finished
|
// to triggerUpdate until this initial refresh is finished
|
||||||
// this is to make sure that when a page is opened in an inactive tab which
|
// 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
|
// has throttled rAF/timers we still immeditately set the page title
|
||||||
if (isUndefined(this.$root._vueMetaInitialized)) {
|
if (isUndefined(this.$root._vueMetaPaused)) {
|
||||||
this.$root._vueMetaInitialized = false
|
this.$root._vueMetaInitialized = this.$isServer
|
||||||
|
|
||||||
this.$root.$options.mounted = this.$root.$options.mounted || []
|
if (!this.$root._vueMetaInitialized) {
|
||||||
this.$root.$options.mounted.push(() => {
|
ensuredPush(this.$options, 'mounted', () => {
|
||||||
if (!this.$root._vueMetaInitialized) {
|
if (!this.$root._vueMetaInitialized) {
|
||||||
this.$nextTick(function () {
|
this.$nextTick(function () {
|
||||||
this.$root.$meta().refresh()
|
this.$root.$meta().refresh()
|
||||||
this.$root._vueMetaInitialized = true
|
this.$root._vueMetaInitialized = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not trigger refresh on the server side
|
// do not trigger refresh on the server side
|
||||||
if (!this.$isServer) {
|
if (!this.$isServer) {
|
||||||
// re-render meta data when returning from a child component to parent
|
// re-render meta data when returning from a child component to parent
|
||||||
this.$options.destroyed = this.$options.destroyed || []
|
ensuredPush(this.$options, 'destroyed', () => {
|
||||||
this.$options.destroyed.push(() => {
|
|
||||||
// Wait that element is hidden before refreshing meta tags (to support animations)
|
// Wait that element is hidden before refreshing meta tags (to support animations)
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
if (this.$el && this.$el.offsetParent !== null) {
|
if (this.$el && this.$el.offsetParent !== null) {
|
||||||
@@ -83,7 +74,7 @@ export default function createMixin(options) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerUpdate(this)
|
triggerUpdate(this, 'destroyed')
|
||||||
}, 50)
|
}, 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', () => ({
|
jest.mock('../package.json', () => ({
|
||||||
version: 'test-version'
|
version: 'test-version'
|
||||||
}))
|
}))
|
||||||
@@ -7,6 +11,7 @@ jest.mock('../package.json', () => ({
|
|||||||
describe('plugin', () => {
|
describe('plugin', () => {
|
||||||
let Vue
|
let Vue
|
||||||
|
|
||||||
|
beforeEach(() => jest.clearAllMocks())
|
||||||
beforeAll(() => (Vue = loadVueMetaPlugin(true)))
|
beforeAll(() => (Vue = loadVueMetaPlugin(true)))
|
||||||
|
|
||||||
test('is loaded', () => {
|
test('is loaded', () => {
|
||||||
@@ -35,4 +40,64 @@ describe('plugin', () => {
|
|||||||
test('plugin sets package version', () => {
|
test('plugin sets package version', () => {
|
||||||
expect(VueMetaBrowserPlugin.version).toBe('test-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
|
return localVue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const vmTick = (vm) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
vm.$nextTick(resolve)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user