8000 Refactored element observables, removed memleaks · squidfunk/mkdocs-material@8a6b89f · GitHub
[go: up one dir, main page]

Skip to content

Commit 8a6b89f

Browse files
committed
Refactored element observables, removed memleaks
1 parent 989b859 commit 8a6b89f

File tree

3 files changed

+73
-17
lines changed

3 files changed

+73
-17
lines changed

src/assets/javascripts/browser/element/focus/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
*/
2222

2323
import { Observable, fromEvent, merge } from "rxjs"
24-
import { map, shareReplay, startWith } from "rxjs/operators"
24+
import { map, startWith } from "rxjs/operators"
2525

2626
import { getActiveElement } from "../_"
2727

@@ -62,7 +62,6 @@ export function watchElementFocus(
6262
)
6363
.pipe(
6464
map(({ type }) => type === "focus"),
65-
startWith(el === getActiveElement()),
66-
shareReplay(1)
65+
startWith(el === getActiveElement())
6766
)
6867
}

src/assets/javascripts/browser/element/offset/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
*/
2222

2323
import { Observable, fromEvent, merge } from "rxjs"
24-
import { map, shareReplay, startWith } from "rxjs/operators"
24+
import { map, startWith } from "rxjs/operators"
2525

2626
/* ----------------------------------------------------------------------------
2727
* Types
@@ -71,7 +71,6 @@ export function watchElementOffset(
7171
)
7272
.pipe(
7373
map(() => getElementOffset(el)),
74-
startWith(getElementOffset(el)),
75-
shareReplay(1)
74+
startWith(getElementOffset(el))
7675
)
7776
}

src/assets/javascripts/browser/element/size/index.ts

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,23 @@
2121
*/
2222

2323
import ResizeObserver from "resize-observer-polyfill"
24-
import { Observable, fromEventPattern } from "rxjs"
25-
import { shareReplay, startWith } from "rxjs/operators"
24+
import {
25+
NEVER,
26+
Observable,
27+
Subject,
28+
defer,
29+
merge,
30+
of
31+
} from "rxjs"
32+
import {
33+
filter,
34+
finalize,
35+
map,
36+
shareReplay,
37+
startWith,
38+
switchMap,
39+
tap
40+
} from "rxjs/operators"
2641

2742
/* ----------------------------------------------------------------------------
2843
* Types
@@ -36,6 +51,40 @@ export interface ElementSize {
3651
height: number /* Element height */
3752
}
3853

54+
/* ----------------------------------------------------------------------------
55+
* Data
56+
* ------------------------------------------------------------------------- */
57+
58+
/**
59+
* Resize observer entry subject
60+
*/
61+
const entry$ = new Subject<ResizeObserverEntry>()
62+
63+
/**
64+
* Resize observer observable
65+
*
66+
* This observable will create a `ResizeObserver` on the first subscription
67+
* and will automatically terminate it when there are no more subscribers.
68+
* It's quite important to centralize observation in a single `ResizeObserver`,
69+
* as the performance difference can be quite dramatic, as the link shows.
70+
*
71+
* @see https://bit.ly/3iIYfEm - Google Groups on performance
72+
*/
73+
const observer$ = defer(() => of(
74+
new ResizeObserver(entries => {
75+
for (const entry of entries)
76+
entry$.next(entry)
77+
})
78+
))
79+
.pipe(
80+
switchMap(resize => merge(of(resize), NEVER)
81+
.pipe(
82+
finalize(() => resize.disconnect())
83+
)
84+
),
85+
shareReplay({ bufferSize: 1, refCount: true })
86+
)
87+
3988
/* ----------------------------------------------------------------------------
4089
* Functions
4190
* ------------------------------------------------------------------------- */
@@ -59,22 +108,31 @@ export function getElementSize(el: HTMLElement): ElementSize {
59108
/**
60109
* Watch element size
61110
*
111+
* This function returns an observable that will subscribe to a single internal
112+
* instance of `ResizeObserver` upon subscription, and emit resize events until
113+
* termination. Note that this function should not be called with the same
114+
* element twice, as the first unsubscription will terminate observation.
115+
*
62116
* @param el - Element
63117
*
64118
* @return Element size observable
65119
*/
66120
export function watchElementSize(
67121
el: HTMLElement
68122
): Observable<ElementSize> {
69-
return fromEventPattern<ElementSize>(next => {
70-
new ResizeObserver(([{ contentRect }]) => next({
71-
width: Math.round(contentRect.width),
72-
height: Math.round(contentRect.height)
73-
}))
74-
.observe(el)
75-
})
123+
return observer$
76124
.pipe(
77-
startWith(getElementSize(el)),
78-
shareReplay(1)
125+
tap(observer => observer.observe(el)),
126+
switchMap(observer => entry$
127+
.pipe(
128+
filter(({ target }) => target === el),
129+
finalize(() => observer.unobserve(el)),
130+
map(({ contentRect }) => ({
131+
width: contentRect.width,
132+
height: contentRect.height
133+
}))
134+
)
135+
),
136+
startWith(getElementSize(el))
79137
)
80138
}

0 commit comments

Comments
 (0)
0