Skip to content

Commit ff455c2

Browse files
committed
fix(capture): implement caching for clone measurements to optimize performance
1 parent c65049b commit ff455c2

1 file changed

Lines changed: 24 additions & 13 deletions

File tree

src/core/capture.js

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -178,20 +178,31 @@ export async function captureDOM(element, options) {
178178
)
179179
if (docH > 0) h0 = Math.max(h0, limitDecimals(docH))
180180
if (docW > 0) w0 = Math.max(w0, limitDecimals(docW))
181-
// Also measure clone in a temp container with injected styles (clone may layout differently)
181+
// Also measure clone in a temp container with injected styles (clone may layout differently).
182+
// PERF-3: cache result per element — this cloneNode(true) + layout round-trip is expensive
183+
// for large DOMs; reuse when the same element is captured with the same total CSS length.
182184
try {
183-
const wrap = elDoc.createElement('div')
184-
wrap.style.cssText = 'position:absolute!important;left:-9999px!important;top:0!important;width:' + w0 + 'px!important;overflow:visible!important;visibility:hidden!important;'
185-
const styleNode = elDoc.createElement('style')
186-
styleNode.textContent = (state.scrollbarCSS || '') + state.baseCSS + state.fontsCSS + 'svg{overflow:visible;} foreignObject{overflow:visible;}' + state.classCSS
187-
wrap.appendChild(styleNode)
188-
wrap.appendChild(state.clone.cloneNode(true))
189-
elDoc.body.appendChild(wrap)
190-
const csh = wrap.scrollHeight
191-
const csw = wrap.scrollWidth
192-
elDoc.body.removeChild(wrap)
193-
if (csh > 0) h0 = Math.max(h0, limitDecimals(csh))
194-
if (csw > 0) w0 = Math.max(w0, limitDecimals(csw))
185+
const cssLen = (state.scrollbarCSS || '').length + (state.baseCSS || '').length +
186+
(state.fontsCSS || '').length + (state.classCSS || '').length
187+
const hint = cache.measureHints.get(state.element)
188+
if (hint && hint.cssLen === cssLen && hint.w0 === w0) {
189+
if (hint.csh > 0) h0 = Math.max(h0, limitDecimals(hint.csh))
190+
if (hint.csw > 0) w0 = Math.max(w0, limitDecimals(hint.csw))
191+
} else {
192+
const wrap = elDoc.createElement('div')
193+
wrap.style.cssText = 'position:absolute!important;left:-9999px!important;top:0!important;width:' + w0 + 'px!important;overflow:visible!important;visibility:hidden!important;'
194+
const styleNode = elDoc.createElement('style')
195+
styleNode.textContent = (state.scrollbarCSS || '') + state.baseCSS + state.fontsCSS + 'svg{overflow:visible;} foreignObject{overflow:visible;}' + state.classCSS
196+
wrap.appendChild(styleNode)
197+
wrap.appendChild(state.clone.cloneNode(true))
198+
elDoc.body.appendChild(wrap)
199+
const csh = wrap.scrollHeight
200+
const csw = wrap.scrollWidth
201+
elDoc.body.removeChild(wrap)
202+
cache.measureHints.set(state.element, { cssLen, w0, csh, csw })
203+
if (csh > 0) h0 = Math.max(h0, limitDecimals(csh))
204+
if (csw > 0) w0 = Math.max(w0, limitDecimals(csw))
205+
}
195206
} catch { /* fallback: use doc dimensions above */ }
196207
}
197208
// === NEW: recompute height using the kept-children span (no offscreen) ===

0 commit comments

Comments
 (0)