|
52 | 52 | let mutationBurstCount = 0 |
53 | 53 | let mutationCooldownUntil = 0 |
54 | 54 |
|
| 55 | + // ----- 调试埋点(默认关闭,开启:localStorage.setItem('__sp_observer_debug__','1') + 刷新) ----- |
| 56 | + |
| 57 | + const PERF_DEBUG = (() => { |
| 58 | + try { |
| 59 | + return localStorage.getItem('__sp_observer_debug__') === '1' |
| 60 | + } catch { |
| 61 | + return false |
| 62 | + } |
| 63 | + })() |
| 64 | + const noop = () => {} |
| 65 | + const perfMark = PERF_DEBUG |
| 66 | + ? name => { |
| 67 | + try { |
| 68 | + performance.mark(name) |
| 69 | + } catch { |
| 70 | + // ignore |
| 71 | + } |
| 72 | + } |
| 73 | + : noop |
| 74 | + const perfMeasure = PERF_DEBUG |
| 75 | + ? (name, startMark, detail) => { |
| 76 | + try { |
| 77 | + performance.measure(name, { start: startMark, detail }) |
| 78 | + } catch { |
| 79 | + // ignore |
| 80 | + } |
| 81 | + try { |
| 82 | + performance.clearMarks(startMark) |
| 83 | + } catch { |
| 84 | + // ignore |
| 85 | + } |
| 86 | + } |
| 87 | + : noop |
| 88 | + |
55 | 89 | // ----- 底层 helper ----- |
56 | 90 |
|
57 | 91 | const trimList = (list, max) => { |
|
256 | 290 | } |
257 | 291 |
|
258 | 292 | const sendSnapshot = () => { |
| 293 | + perfMark('sp:send-start') |
259 | 294 | const runtime = getRuntime() |
260 | 295 | if (stopped || !runtime) { |
261 | 296 | stopObserver() |
|
282 | 317 | } catch (error) { |
283 | 318 | handleSendFailure(error) |
284 | 319 | } |
| 320 | + perfMeasure('sp:send-snapshot', 'sp:send-start', { |
| 321 | + resources: state.resources.length, |
| 322 | + scripts: state.scripts.length, |
| 323 | + stylesheets: state.stylesheets.length, |
| 324 | + iframes: state.iframes.length, |
| 325 | + domMarkers: state.domMarkers.length |
| 326 | + }) |
285 | 327 | } |
286 | 328 |
|
287 | 329 | const scheduleSend = () => { |
|
386 | 428 | try { |
387 | 429 | const observer = new PerformanceObserver(list => { |
388 | 430 | if (stopped) return |
| 431 | + perfMark('sp:po-start') |
389 | 432 | let added = 0 |
390 | | - for (const entry of list.getEntries()) { |
| 433 | + const entries = list.getEntries() |
| 434 | + for (const entry of entries) { |
391 | 435 | if (SKIP_INITIATOR_TYPES.has(entry.initiatorType)) continue |
392 | 436 | if (SKIP_RESOURCE_EXT.test(entry.name)) continue |
393 | 437 | if (addUrl('resources', entry.name)) added += 1 |
|
398 | 442 | performanceObserver = null |
399 | 443 | } |
400 | 444 | if (added) scheduleSend() |
| 445 | + perfMeasure('sp:perf-observer', 'sp:po-start', { entries: entries.length, added }) |
401 | 446 | }) |
402 | 447 | performanceObserver = observer |
403 | 448 | observer.observe({ type: 'resource', buffered: true }) |
|
412 | 457 | const nodes = pendingMutationNodes |
413 | 458 | pendingMutationNodes = [] |
414 | 459 | if (!nodes.length) return |
| 460 | + perfMark('sp:flush-start') |
415 | 461 | let changed = false |
| 462 | + let processed = 0 |
416 | 463 | for (const node of nodes) { |
417 | 464 | if (!node.isConnected) continue |
| 465 | + processed += 1 |
418 | 466 | changed = collectFromElement(node) || changed |
419 | 467 | } |
420 | 468 | if (changed) { |
421 | 469 | state.updatedAt = Date.now() |
422 | 470 | scheduleSend() |
423 | 471 | } |
| 472 | + perfMeasure('sp:mutation-flush', 'sp:flush-start', { queued: nodes.length, processed, changed }) |
424 | 473 | } |
425 | 474 |
|
426 | 475 | const scheduleMutationFlush = () => { |
|
443 | 492 | if (stopped) return |
444 | 493 | const now = Date.now() |
445 | 494 | if (now < mutationCooldownUntil) return |
| 495 | + perfMark('sp:mo-start') |
446 | 496 | if (now - mutationBurstWindowStart > MUTATION_BURST_WINDOW_MS) { |
447 | 497 | mutationBurstWindowStart = now |
448 | 498 | mutationBurstCount = 0 |
449 | 499 | } |
| 500 | + const pendingBefore = pendingMutationNodes.length |
450 | 501 | let pendingFull = false |
451 | 502 | outer: for (const mutation of mutations) { |
452 | 503 | for (const node of mutation.addedNodes) { |
|
462 | 513 | } |
463 | 514 | } |
464 | 515 | } |
| 516 | + const accepted = pendingMutationNodes.length - pendingBefore |
465 | 517 | if (pendingFull || mutationBurstCount >= MUTATION_BURST_THRESHOLD) { |
466 | 518 | triggerMutationCooldown(now) |
| 519 | + perfMeasure('sp:mutation-callback', 'sp:mo-start', { |
| 520 | + mutations: mutations.length, |
| 521 | + accepted, |
| 522 | + cooldown: true, |
| 523 | + pendingFull |
| 524 | + }) |
467 | 525 | return |
468 | 526 | } |
469 | 527 | if (state.mutationCount >= MAX_MUTATION_COUNT) { |
470 | 528 | observer.disconnect() |
471 | 529 | mutationObserver = null |
472 | 530 | } |
473 | 531 | if (pendingMutationNodes.length) scheduleMutationFlush() |
| 532 | + perfMeasure('sp:mutation-callback', 'sp:mo-start', { mutations: mutations.length, accepted, cooldown: false }) |
474 | 533 | }) |
475 | 534 | mutationObserver = observer |
476 | 535 | observer.observe(root, { childList: true, subtree: true }) |
|
0 commit comments