Skip to content

Commit

Permalink
feat: add options debounceWait
Browse files Browse the repository at this point in the history
  • Loading branch information
pimlie committed Sep 17, 2019
1 parent 54ea6c6 commit d43b77c
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 13 deletions.
12 changes: 8 additions & 4 deletions src/client/update.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { rootConfigKey } from '../shared/constants'
// store an id to keep track of DOM updates
let batchId = null

export function triggerUpdate (rootVm, hookName) {
export function triggerUpdate ({ debounceWait }, 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
Expand All @@ -13,7 +13,7 @@ export function triggerUpdate (rootVm, hookName) {

if (rootVm[rootConfigKey].initialized && !rootVm[rootConfigKey].pausing) {
// batch potential DOM updates to prevent extraneous re-rendering
batchUpdate(() => rootVm.$meta().refresh())
batchUpdate(() => void rootVm.$meta().refresh(), debounceWait)
}
}

Expand All @@ -25,10 +25,14 @@ export function triggerUpdate (rootVm, hookName) {
* @return {Number} id - a new ID
*/
export function batchUpdate (callback, timeout) {
timeout = timeout || 10
timeout = timeout === undefined ? 10 : timeout

clearTimeout(batchId)
if (!timeout) {
callback()
return
}

clearTimeout(batchId)
batchId = setTimeout(() => {
callback()
}, timeout)
Expand Down
8 changes: 8 additions & 0 deletions src/shared/$meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ export default function $meta (options) {
options.refreshOnceOnNavigation = newOptions[refreshNavKey]
addNavGuards($root)
}

const debounceWaitKey = 'debounceWait'
if (newOptions && newOptions[debounceWaitKey]) {
const debounceWait = parseInt(newOptions[debounceWaitKey])
if (!isNaN(debounceWait)) {
options.debounceWait = debounceWait
}
}
},
'refresh': () => refresh($root, options),
'inject': () => process.server ? inject($root, options) : showWarningNotSupportedInBrowserBundle('inject'),
Expand Down
4 changes: 4 additions & 0 deletions src/shared/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,17 @@ export const contentKeyName = 'content'
// The id used for the ssr app
export const ssrAppId = 'ssr'

// How long meta update
export const debounceWait = 10

export const defaultOptions = {
keyName,
attribute,
ssrAttribute,
tagIDKeyName,
contentKeyName,
metaTemplateKeyName,
debounceWait,
ssrAppId
}

Expand Down
9 changes: 4 additions & 5 deletions src/shared/mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default function createMixin (Vue, options) {
// credit for this suggestion goes to [Sébastien Chopin](https://github.com/Atinux)
ensuredPush($options, 'created', function () {
this.$watch('$metaInfo', function () {
triggerUpdate(this[rootKey], 'watcher')
triggerUpdate(options, this[rootKey], 'watcher')
})
})
}
Expand Down Expand Up @@ -112,7 +112,7 @@ export default function createMixin (Vue, options) {
// current hook was called
// (during initialization all changes are blocked)
if (tags === false && $root[rootConfigKey].initialized === null) {
this.$nextTick(() => triggerUpdate($root, 'init'))
this.$nextTick(() => triggerUpdate(options, $root, 'init'))
}

$root[rootConfigKey].initialized = true
Expand All @@ -126,7 +126,6 @@ export default function createMixin (Vue, options) {
})
}
})

// add the navigation guards if requested
if (options.refreshOnceOnNavigation) {
addNavGuards($root)
Expand All @@ -142,7 +141,7 @@ export default function createMixin (Vue, options) {
// no need to add this hooks on server side
updateOnLifecycleHook.forEach((lifecycleHook) => {
ensuredPush($options, lifecycleHook, function () {
triggerUpdate(this[rootKey], lifecycleHook)
triggerUpdate(options, this[rootKey], lifecycleHook)
})
})
},
Expand All @@ -165,7 +164,7 @@ export default function createMixin (Vue, options) {

clearInterval(interval)

triggerUpdate($this.$root, 'destroyed')
triggerUpdate(options, $this.$root, 'destroyed')
}, 50)
}
}
Expand Down
1 change: 1 addition & 0 deletions src/shared/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function setOptions (options) {
tagIDKeyName: options['tagIDKeyName'] || defaultOptions.tagIDKeyName,
contentKeyName: options['contentKeyName'] || defaultOptions.contentKeyName,
metaTemplateKeyName: options['metaTemplateKeyName'] || defaultOptions.metaTemplateKeyName,
debounceWait: options['debounceWait'] || defaultOptions.debounceWait,
ssrAppId: options['ssrAppId'] || defaultOptions.ssrAppId,
refreshOnceOnNavigation: !!options['refreshOnceOnNavigation']
}
Expand Down
12 changes: 11 additions & 1 deletion test/unit/components.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ describe('components', () => {
wrapper.destroy()
})

test('can toggle refreshOnceOnNavigation runtime', () => {
test('can enable option refreshOnceOnNavigation runtime', () => {
const guards = {}
const wrapper = mount(HelloWorld, {
localVue: Vue,
Expand All @@ -503,4 +503,14 @@ describe('components', () => {
expect(guards.before).not.toBeUndefined()
expect(guards.after).not.toBeUndefined()
})

test('can set option debounceWait runtime', () => {
const wrapper = mount(HelloWorld, { localVue: Vue })

expect(wrapper.vm.$meta().getOptions().debounceWait).toBe(10)

wrapper.vm.$meta().setOptions({ debounceWait: 69420 })

expect(wrapper.vm.$meta().getOptions().debounceWait).toBe(69420)
})
})
6 changes: 3 additions & 3 deletions test/unit/plugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ describe('plugin', () => {
const batchUpdateSpy = batchUpdate.mockImplementation(_batchUpdate)
// because triggerUpdate & batchUpdate reside in the same file we cant mock them both,
// so just recreate the triggerUpdate fn by copying its implementation
const triggerUpdateSpy = triggerUpdate.mockImplementation((vm, hookName) => {
const triggerUpdateSpy = triggerUpdate.mockImplementation((options, vm, hookName) => {
if (vm.$root._vueMeta.initialized && !vm.$root._vueMeta.pausing) {
// batch potential DOM updates to prevent extraneous re-rendering
batchUpdateSpy(() => vm.$meta().refresh())
Expand Down Expand Up @@ -216,15 +216,15 @@ describe('plugin', () => {
expect(metaInfo.title).toBe(title)
})

test('updates are batched', async () => {
test('updates are batched by default', async () => {
jest.useFakeTimers()

const { batchUpdate: _batchUpdate } = jest.requireActual('../../src/client/update')
const batchUpdateSpy = batchUpdate.mockImplementation(_batchUpdate)
const refreshSpy = jest.fn()
// because triggerUpdate & batchUpdate reside in the same file we cant mock them both,
// so just recreate the triggerUpdate fn by copying its implementation
triggerUpdate.mockImplementation((vm, hookName) => {
triggerUpdate.mockImplementation((options, vm, hookName) => {
if (vm.$root._vueMeta.initialized && !vm.$root._vueMeta.pausing) {
// batch potential DOM updates to prevent extraneous re-rendering
batchUpdateSpy(refreshSpy)
Expand Down
30 changes: 30 additions & 0 deletions test/unit/shared.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { setOptions } from '../../src/shared/options'
import { defaultOptions } from '../../src/shared/constants'
import { triggerUpdate } from '../../src/client/update'

describe('shared', () => {
test('can use setOptions', () => {
Expand All @@ -11,4 +12,33 @@ describe('shared', () => {
expect(options.contentKeyName).toBeDefined()
expect(options.contentKeyName).toBe(defaultOptions.contentKeyName)
})

test('options.debounceWait is used', () => {
jest.useFakeTimers()

const refresh = jest.fn()
const componentMock = {
_vueMeta: {
initialized: true,
pausing: false
},
$meta: () => ({ refresh })
}

triggerUpdate({ debounceWait: 0 }, componentMock, 'test')

expect(refresh).toHaveBeenCalledTimes(1)

triggerUpdate({}, componentMock, 'test')
expect(refresh).toHaveBeenCalledTimes(1)
jest.advanceTimersByTime(11)
expect(refresh).toHaveBeenCalledTimes(2)

triggerUpdate({ debounceWait: 69420 }, componentMock, 'test')
expect(refresh).toHaveBeenCalledTimes(2)
jest.advanceTimersByTime(11)
expect(refresh).toHaveBeenCalledTimes(2)
jest.advanceTimersByTime(69500)
expect(refresh).toHaveBeenCalledTimes(3)
})
})

0 comments on commit d43b77c

Please sign in to comment.