Skip to content

Commit f745059

Browse files
committed
feat: add option waitOnDestroyed
1 parent d43b77c commit f745059

File tree

7 files changed

+102
-19
lines changed

7 files changed

+102
-19
lines changed

src/shared/$meta.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,22 @@ export default function $meta (options) {
2020
'setOptions': (newOptions) => {
2121
const refreshNavKey = 'refreshOnceOnNavigation'
2222
if (newOptions && newOptions[refreshNavKey]) {
23-
options.refreshOnceOnNavigation = newOptions[refreshNavKey]
23+
options.refreshOnceOnNavigation = !!newOptions[refreshNavKey]
2424
addNavGuards($root)
2525
}
2626

2727
const debounceWaitKey = 'debounceWait'
28-
if (newOptions && newOptions[debounceWaitKey]) {
28+
if (newOptions && debounceWaitKey in newOptions) {
2929
const debounceWait = parseInt(newOptions[debounceWaitKey])
3030
if (!isNaN(debounceWait)) {
3131
options.debounceWait = debounceWait
3232
}
3333
}
34+
35+
const waitOnDestroyedKey = 'waitOnDestroyed'
36+
if (newOptions && waitOnDestroyedKey in newOptions) {
37+
options.waitOnDestroyed = !!newOptions[waitOnDestroyedKey]
38+
}
3439
},
3540
'refresh': () => refresh($root, options),
3641
'inject': () => process.server ? inject($root, options) : showWarningNotSupportedInBrowserBundle('inject'),

src/shared/constants.js

+4
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,17 @@ export const ssrAppId = 'ssr'
5252
// How long meta update
5353
export const debounceWait = 10
5454

55+
// How long meta update
56+
export const waitOnDestroyed = true
57+
5558
export const defaultOptions = {
5659
keyName,
5760
attribute,
5861
ssrAttribute,
5962
tagIDKeyName,
6063
contentKeyName,
6164
metaTemplateKeyName,
65+
waitOnDestroyed,
6266
debounceWait,
6367
ssrAppId
6468
}

src/shared/mixin.js

+16-9
Original file line numberDiff line numberDiff line change
@@ -147,25 +147,32 @@ export default function createMixin (Vue, options) {
147147
},
148148
// TODO: move back into beforeCreate when Vue issue is resolved
149149
destroyed () {
150-
const $this = this
151150
// do not trigger refresh:
151+
// - when user configured to not wait for transitions on destroyed
152152
// - when the component doesnt have a parent
153153
// - doesnt have metaInfo defined
154-
if (!$this.$parent || !hasMetaInfo($this)) {
154+
if (!this.$parent || !hasMetaInfo(this)) {
155155
return
156156
}
157157

158-
// Wait that element is hidden before refreshing meta tags (to support animations)
159-
const interval = setInterval(() => {
160-
if ($this.$el && $this.$el.offsetParent !== null) {
161-
/* istanbul ignore next line */
158+
this.$nextTick(() => {
159+
if (!options.waitOnDestroyed || !this.$el || !this.$el.offsetParent) {
160+
triggerUpdate(options, this.$root, 'destroyed')
162161
return
163162
}
164163

165-
clearInterval(interval)
164+
// Wait that element is hidden before refreshing meta tags (to support animations)
165+
const interval = setInterval(() => {
166+
if (this.$el && this.$el.offsetParent !== null) {
167+
/* istanbul ignore next line */
168+
return
169+
}
170+
171+
clearInterval(interval)
166172

167-
triggerUpdate(options, $this.$root, 'destroyed')
168-
}, 50)
173+
triggerUpdate(options, this.$root, 'destroyed')
174+
}, 50)
175+
})
169176
}
170177
}
171178
}

src/shared/options.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isObject } from '../utils/is-type'
1+
import { isObject, isUndefined } from '../utils/is-type'
22
import { defaultOptions } from './constants'
33

44
export function setOptions (options) {
@@ -17,7 +17,8 @@ export function setOptions (options) {
1717
tagIDKeyName: options['tagIDKeyName'] || defaultOptions.tagIDKeyName,
1818
contentKeyName: options['contentKeyName'] || defaultOptions.contentKeyName,
1919
metaTemplateKeyName: options['metaTemplateKeyName'] || defaultOptions.metaTemplateKeyName,
20-
debounceWait: options['debounceWait'] || defaultOptions.debounceWait,
20+
debounceWait: isUndefined(options['debounceWait']) ? defaultOptions.debounceWait : options['debounceWait'],
21+
waitOnDestroyed: isUndefined(options['waitOnDestroyed']) ? defaultOptions.waitOnDestroyed : options['waitOnDestroyed'],
2122
ssrAppId: options['ssrAppId'] || defaultOptions.ssrAppId,
2223
refreshOnceOnNavigation: !!options['refreshOnceOnNavigation']
2324
}

test/unit/components.test.js

+51-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { getComponentMetaInfo } from '../../src/shared/getComponentOption'
22
import _getMetaInfo from '../../src/shared/getMetaInfo'
3+
import { triggerUpdate, batchUpdate } from '../../src/client/update'
34
import { mount, createWrapper, loadVueMetaPlugin, vmTick, clearClientAttributeMap } from '../utils'
45
import { defaultOptions } from '../../src/shared/constants'
56

@@ -10,6 +11,7 @@ import Changed from '../components/changed.vue'
1011

1112
const getMetaInfo = component => _getMetaInfo(defaultOptions, getComponentMetaInfo(defaultOptions, component))
1213

14+
jest.mock('../../src/client/update')
1315
jest.mock('../../src/utils/window', () => ({
1416
hasGlobalWindow: false
1517
}))
@@ -48,6 +50,8 @@ describe('components', () => {
4850
})
4951

5052
afterEach(() => {
53+
jest.clearAllMocks()
54+
5155
elements.html.getAttributeNames().forEach(name => elements.html.removeAttribute(name))
5256
elements.head.childNodes.forEach(child => child.remove())
5357
elements.head.getAttributeNames().forEach(name => elements.head.removeAttribute(name))
@@ -209,6 +213,10 @@ describe('components', () => {
209213
})
210214

211215
test('changed function is called', async () => {
216+
const update = jest.requireActual('../../src/client/update')
217+
triggerUpdate.mockImplementation(update.triggerUpdate)
218+
batchUpdate.mockImplementation(update.batchUpdate)
219+
212220
let context
213221
const changed = jest.fn(function () {
214222
context = this
@@ -226,6 +234,9 @@ describe('components', () => {
226234

227235
expect(changed).toHaveBeenCalledTimes(2)
228236
expect(context._uid).toBe(wrapper.vm._uid)
237+
238+
triggerUpdate.mockRestore()
239+
batchUpdate.mockRestore()
229240
})
230241

231242
test('afterNavigation function is called with refreshOnce: true', async () => {
@@ -299,6 +310,10 @@ describe('components', () => {
299310
})
300311

301312
test('changes before hydration initialization trigger an update', async () => {
313+
const update = jest.requireActual('../../src/client/update')
314+
triggerUpdate.mockImplementation(update.triggerUpdate)
315+
batchUpdate.mockImplementation(update.batchUpdate)
316+
302317
const { html } = elements
303318
html.setAttribute(defaultOptions.ssrAttribute, 'true')
304319

@@ -342,9 +357,16 @@ describe('components', () => {
342357

343358
expect(html.getAttribute('theme')).toBe('dark')
344359
wrapper.destroy()
360+
361+
triggerUpdate.mockRestore()
362+
batchUpdate.mockRestore()
345363
})
346364

347365
test('changes during hydration initialization trigger an update', async () => {
366+
const update = jest.requireActual('../../src/client/update')
367+
triggerUpdate.mockImplementation(update.triggerUpdate)
368+
batchUpdate.mockImplementation(update.batchUpdate)
369+
348370
const { html } = elements
349371
html.setAttribute(defaultOptions.ssrAttribute, 'true')
350372

@@ -386,6 +408,9 @@ describe('components', () => {
386408

387409
expect(html.getAttribute('theme')).toBe('dark')
388410
wrapper.destroy()
411+
412+
triggerUpdate.mockRestore()
413+
batchUpdate.mockRestore()
389414
})
390415

391416
test('can add/remove meta info from additional app ', () => {
@@ -504,13 +529,34 @@ describe('components', () => {
504529
expect(guards.after).not.toBeUndefined()
505530
})
506531

507-
test('can set option debounceWait runtime', () => {
508-
const wrapper = mount(HelloWorld, { localVue: Vue })
532+
test('destroyed hook calls triggerUpdate delayed', async () => {
533+
jest.useFakeTimers()
534+
const wrapper = mount(HelloWorld, { localVue: Vue, parentComponent: { render: h => h('div') } })
535+
const spy = jest.spyOn(wrapper.vm.$el, 'offsetParent', 'get').mockReturnValue(true)
509536

510-
expect(wrapper.vm.$meta().getOptions().debounceWait).toBe(10)
537+
wrapper.destroy()
511538

512-
wrapper.vm.$meta().setOptions({ debounceWait: 69420 })
539+
await vmTick(wrapper.vm)
540+
541+
expect(triggerUpdate).toHaveBeenCalledTimes(1)
542+
spy.mockRestore()
543+
544+
jest.advanceTimersByTime(51)
545+
546+
expect(triggerUpdate).toHaveBeenCalledTimes(2)
547+
expect(triggerUpdate).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), 'destroyed')
548+
})
549+
550+
test('destroyed hook calls triggerUpdate immediately when waitOnDestroyed: false', async () => {
551+
jest.useFakeTimers()
552+
553+
const wrapper = mount(HelloWorld, { localVue: Vue, parentComponent: { render: h => h('div') } })
554+
wrapper.vm.$meta().setOptions({ waitOnDestroyed: false })
555+
wrapper.destroy()
556+
557+
await vmTick(wrapper.vm)
513558

514-
expect(wrapper.vm.$meta().getOptions().debounceWait).toBe(69420)
559+
expect(triggerUpdate).toHaveBeenCalledTimes(2)
560+
expect(triggerUpdate).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), 'destroyed')
515561
})
516562
})

test/unit/generators.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ describe('generators', () => {
6161
}
6262
})
6363

64-
describe.only('extra tests', () => {
64+
describe('extra tests', () => {
6565
test('empty config doesnt generate a tag', () => {
6666
const { meta } = generateServerInjector({ meta: [] })
6767

test/unit/plugin.test.js

+20
Original file line numberDiff line numberDiff line change
@@ -263,4 +263,24 @@ describe('plugin', () => {
263263
jest.advanceTimersByTime(10)
264264
expect(refreshSpy).toHaveBeenCalled()
265265
})
266+
267+
test('can set option waitOnDestroyed runtime', () => {
268+
const wrapper = mount({ render: h => h('div') }, { localVue: Vue })
269+
270+
expect(wrapper.vm.$meta().getOptions().waitOnDestroyed).toBe(true)
271+
272+
wrapper.vm.$meta().setOptions({ waitOnDestroyed: false })
273+
274+
expect(wrapper.vm.$meta().getOptions().waitOnDestroyed).toBe(false)
275+
})
276+
277+
test('can set option debounceWait runtime', () => {
278+
const wrapper = mount({ render: h => h('div') }, { localVue: Vue })
279+
280+
expect(wrapper.vm.$meta().getOptions().debounceWait).toBe(10)
281+
282+
wrapper.vm.$meta().setOptions({ debounceWait: 69420 })
283+
284+
expect(wrapper.vm.$meta().getOptions().debounceWait).toBe(69420)
285+
})
266286
})

0 commit comments

Comments
 (0)