Skip to content

Commit c97ec00

Browse files
committedFeb 3, 2025·
fix!: property debounce dom updates
1 parent bb8cda6 commit c97ec00

File tree

6 files changed

+34
-18
lines changed

6 files changed

+34
-18
lines changed
 

‎packages/angular/src/unhead/install.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { InjectionToken, makeEnvironmentProviders } from '@angular/core'
44
import { BEFORE_APP_SERIALIZED } from '@angular/platform-server'
55
import { createHead as _createClientHead } from 'unhead/client'
66
import { createHead as _createServerHead } from 'unhead/server'
7+
import { createDebouncedDomRender } from 'unhead/src/client'
78
import { Unhead } from '../lib/unhead.service'
89
import { ReactivityPlugin } from '../unhead/ReactivityPlugin'
910

@@ -36,7 +37,7 @@ export function provideServerHead(options: Omit<CreateHeadOptions, 'domDelayFn'
3637
export function provideClientHead(options: Omit<CreateHeadOptions, 'domOptions' | 'document'> = {}) {
3738
const head = _createClientHead<AngularUnhead>({
3839
domOptions: {
39-
delayFn: fn => setTimeout(() => fn(), 10),
40+
render: createDebouncedDomRender(fn => setTimeout(() => fn(), 0)),
4041
},
4142
...options,
4243
plugins: [

‎packages/schema/src/plugins.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Unhead } from '@unhead/schema/src/head'
2+
13
export interface RenderDomHeadOptions {
24
/**
35
* Document to use for rendering. Allows stubbing for testing.
@@ -6,5 +8,5 @@ export interface RenderDomHeadOptions {
68
}
79

810
export interface DomPluginOptions extends RenderDomHeadOptions {
9-
delayFn?: (fn: () => void) => void
11+
render: ((head: Unhead<any>) => Promise<void>)
1012
}

‎packages/unhead/src/client/createHead.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@ import { IsBrowser } from '@unhead/shared'
33
import { createHeadCore } from '../createHead'
44
import { DomPlugin } from './plugins/domPlugin'
55
import { ClientEventHandlerPlugin } from './plugins/eventHandlers'
6+
import { renderDOMHead } from './renderDOMHead'
67

78
export function createHead<T extends Record<string, any> = Head>(options: CreateClientHeadOptions = {}) {
89
return createHeadCore<T>({
910
document: (IsBrowser ? document : undefined),
1011
...options,
1112
plugins: [
1213
...(options.plugins || []),
13-
DomPlugin(options.domOptions),
14+
DomPlugin({
15+
render: renderDOMHead,
16+
...options.domOptions,
17+
}),
1418
ClientEventHandlerPlugin,
1519
],
1620
})
+20-10
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
1-
import type { DomPluginOptions, Unhead } from '@unhead/schema'
1+
import type { Unhead } from '@unhead/schema'
22
import { renderDOMHead } from './renderDOMHead'
33

44
/**
55
* Queue a debounced update of the DOM head.
66
*/
7-
export function debouncedRenderDOMHead<T extends Unhead<any>>(head: T, options: DomPluginOptions = {}) {
8-
const fn = options.delayFn || (fn => setTimeout(fn, 10))
9-
return head._domDebouncedUpdatePromise = head._domDebouncedUpdatePromise || new Promise<void>(resolve => fn(() => {
10-
return renderDOMHead(head, options)
11-
.then(() => {
12-
delete head._domDebouncedUpdatePromise
13-
resolve()
14-
})
15-
}))
7+
export function createDebouncedDomRender(delayFunction: (fn: () => void) => void): ((head: Unhead<any>) => Promise<void>) {
8+
let pendingCallback: (() => void) | null = null
9+
10+
return (head: Unhead<any>) => {
11+
if (pendingCallback) {
12+
pendingCallback = null
13+
}
14+
15+
return head._domDebouncedUpdatePromise = head._domDebouncedUpdatePromise || new Promise<void>((resolve) => {
16+
pendingCallback = () => {
17+
renderDOMHead(head)
18+
.then(() => {
19+
delete head._domDebouncedUpdatePromise
20+
resolve()
21+
})
22+
}
23+
delayFunction(pendingCallback)
24+
})
25+
}
1626
}

‎packages/unhead/src/client/plugins/domPlugin.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { DomPluginOptions } from '@unhead/schema'
22
import { defineHeadPlugin } from '@unhead/shared'
3-
import { debouncedRenderDOMHead } from '../debounced'
43

5-
export function DomPlugin(options?: DomPluginOptions) {
4+
export function DomPlugin(options: DomPluginOptions) {
65
return defineHeadPlugin((head) => {
76
// restore initial entry from payload (titleTemplate and templateParams)
87
const initialPayload = head.resolvedOptions.document?.head.querySelector('script[id="unhead:payload"]')?.innerHTML || false
@@ -14,7 +13,7 @@ export function DomPlugin(options?: DomPluginOptions) {
1413
hooks: {
1514
'entries:updated': (head) => {
1615
// async load the renderDOMHead function
17-
debouncedRenderDOMHead(head, options)
16+
options.render(head)
1817
},
1918
},
2019
}

‎packages/vue/src/client.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { CreateClientHeadOptions, MergeHead } from '@unhead/schema'
22
import type { MaybeComputedRef, ReactiveHead, VueHeadClient } from '@unhead/vue'
3-
import { createHead as _createHead } from 'unhead/client'
3+
import { createHead as _createHead, createDebouncedDomRender } from 'unhead/client'
44
import { nextTick } from 'vue'
55
import { vueInstall } from './install'
66
import { VueReactivityPlugin } from './VueReactivityPlugin'
@@ -11,7 +11,7 @@ export * from 'unhead/client'
1111
export function createHead<T extends MergeHead>(options: CreateClientHeadOptions = {}): VueHeadClient<T> {
1212
const head = _createHead<MaybeComputedRef<ReactiveHead<T>>>({
1313
domOptions: {
14-
delayFn: fn => nextTick(() => setTimeout(() => fn(), 0)),
14+
render: createDebouncedDomRender(nextTick),
1515
},
1616
...options,
1717
plugins: [

0 commit comments

Comments
 (0)