Skip to content

Commit cd609ce

Browse files
committed
perf: 削减动态监控热路径开销
合并跳过容器匹配为单条正则、移除待处理节点的 O(N²) 祖先检查、待处理节点超 200 直接进 cooldown、burst 阈值 300→150、idle 回调改回 200ms setTimeout 防止积压一秒后大批量长任务。将版本号提升到 1.2.79。
1 parent 3e2eb7a commit cd609ce

2 files changed

Lines changed: 26 additions & 51 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "stackprism",
33
"private": true,
4-
"version": "1.2.78",
4+
"version": "1.2.79",
55
"type": "module",
66
"description": "StackPrism 用于检测网页前端、后端、CDN、SaaS、广告营销、统计、登录、支付、网站程序和主题模板线索。",
77
"scripts": {

src/content/content-observer.ts

Lines changed: 25 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,19 @@
44
const MAX_DOM_MARKERS = 120
55
const MAX_MUTATION_COUNT = 5000
66
const MAX_RESOURCE_COUNT = 1500
7+
const MAX_PENDING_MUTATION_NODES = 200
78
const SEND_DELAY = 900
89
const MUTATION_BURST_WINDOW_MS = 1000
9-
const MUTATION_BURST_THRESHOLD = 300
10+
const MUTATION_BURST_THRESHOLD = 150
1011
const MUTATION_COOLDOWN_MS = 5000
12+
const MUTATION_FLUSH_DELAY = 200
1113
const CONTEXT_INVALIDATED_PATTERN = /extension context invalidated|context invalidated/i
1214
const OBSERVER_INSTANCE_KEY = '__stackPrismContentObserver__'
1315
const SKIP_TAGS = new Set(['VIDEO', 'AUDIO', 'CANVAS', 'PICTURE', 'SOURCE', 'TRACK', 'SVG', 'IMG'])
1416
const SKIP_INITIATOR_TYPES = new Set(['img', 'video', 'audio', 'beacon', 'track', 'object', 'embed', 'css'])
1517
const SKIP_RESOURCE_EXT = /\.(ts|m4s|mp4|webm|mov|m3u8|mpd|jpg|jpeg|png|gif|webp|avif|ico|woff2?|ttf|otf|eot)(\?.*)?$/i
16-
const SKIP_CONTAINER_NAMES = [
17-
/danmaku/i,
18-
/bullet[\s_-]*(comment|screen|chat)/i,
19-
/barrage/i,
20-
/(^|[\s._#-])chat([\s._#-]|$)/i,
21-
/chat-?(panel|area|list|box|room|stream|window)/i,
22-
/live-?chat/i,
23-
/comment-?(stream|live|list)/i,
24-
/(^|[\s._-])feed([\s._-]|$)/i,
25-
/webcast/i
26-
]
18+
const SKIP_CONTAINER_PATTERN =
19+
/danmaku|bullet[\s_-]*(?:comment|screen|chat)|barrage|(?:^|[\s._#-])chat(?:[\s._#-]|$)|chat-?(?:panel|area|list|box|room|stream|window)|live-?chat|comment-?(?:stream|live|list)|(?:^|[\s._-])feed(?:[\s._-]|$)|webcast/i
2720
const state = {
2821
startedAt: Date.now(),
2922
updatedAt: Date.now(),
@@ -218,18 +211,10 @@
218211
'script[src], link[href], iframe[src], [data-v-app], [ng-version], [data-reactroot], [data-turbo], [data-controller], astro-island, astro-slot'
219212

220213
const matchesSkipContainer = element => {
221-
const tokens = []
222214
const id = element.id
223-
if (id) tokens.push(id)
215+
if (id && SKIP_CONTAINER_PATTERN.test(id)) return true
224216
const className = typeof element.className === 'string' ? element.className : element.getAttribute?.('class') || ''
225-
if (className) {
226-
for (const piece of className.split(/\s+/)) if (piece) tokens.push(piece)
227-
}
228-
if (!tokens.length) return false
229-
for (const re of SKIP_CONTAINER_NAMES) {
230-
for (const token of tokens) if (re.test(token)) return true
231-
}
232-
return false
217+
return Boolean(className && SKIP_CONTAINER_PATTERN.test(className))
233218
}
234219

235220
const collectFromElement = element => {
@@ -343,11 +328,7 @@
343328
stopped = true
344329
clearTimeout(sendTimer)
345330
if (pendingMutationFrame) {
346-
if (typeof cancelIdleCallback === 'function') {
347-
cancelIdleCallback(pendingMutationFrame)
348-
} else {
349-
clearTimeout(pendingMutationFrame)
350-
}
331+
clearTimeout(pendingMutationFrame)
351332
pendingMutationFrame = 0
352333
}
353334
pendingMutationNodes = []
@@ -431,19 +412,9 @@
431412
const nodes = pendingMutationNodes
432413
pendingMutationNodes = []
433414
if (!nodes.length) return
434-
const processed = []
435415
let changed = false
436416
for (const node of nodes) {
437417
if (!node.isConnected) continue
438-
let containedByAncestor = false
439-
for (let i = 0; i < processed.length; i++) {
440-
if (processed[i].contains(node)) {
441-
containedByAncestor = true
442-
break
443-
}
444-
}
445-
if (containedByAncestor) continue
446-
processed.push(node)
447418
changed = collectFromElement(node) || changed
448419
}
449420
if (changed) {
@@ -454,10 +425,15 @@
454425

455426
const scheduleMutationFlush = () => {
456427
if (pendingMutationFrame || stopped) return
457-
if (typeof requestIdleCallback === 'function') {
458-
pendingMutationFrame = requestIdleCallback(processPendingMutationNodes, { timeout: 1000 })
459-
} else {
460-
pendingMutationFrame = setTimeout(processPendingMutationNodes, 200)
428+
pendingMutationFrame = setTimeout(processPendingMutationNodes, MUTATION_FLUSH_DELAY)
429+
}
430+
431+
const triggerMutationCooldown = now => {
432+
mutationCooldownUntil = now + MUTATION_COOLDOWN_MS
433+
pendingMutationNodes = []
434+
if (pendingMutationFrame) {
435+
clearTimeout(pendingMutationFrame)
436+
pendingMutationFrame = 0
461437
}
462438
}
463439

@@ -471,24 +447,23 @@
471447
mutationBurstWindowStart = now
472448
mutationBurstCount = 0
473449
}
474-
for (const mutation of mutations) {
450+
let pendingFull = false
451+
outer: for (const mutation of mutations) {
475452
for (const node of mutation.addedNodes) {
476453
if (node.nodeType !== Node.ELEMENT_NODE) continue
477454
if (SKIP_TAGS.has(node.tagName)) continue
478455
if (matchesSkipContainer(node)) continue
479456
state.mutationCount += 1
480457
mutationBurstCount += 1
481458
pendingMutationNodes.push(node)
459+
if (pendingMutationNodes.length >= MAX_PENDING_MUTATION_NODES) {
460+
pendingFull = true
461+
break outer
462+
}
482463
}
483464
}
484-
if (mutationBurstCount >= MUTATION_BURST_THRESHOLD) {
485-
mutationCooldownUntil = now + MUTATION_COOLDOWN_MS
486-
pendingMutationNodes = []
487-
if (pendingMutationFrame) {
488-
if (typeof cancelIdleCallback === 'function') cancelIdleCallback(pendingMutationFrame)
489-
else clearTimeout(pendingMutationFrame)
490-
pendingMutationFrame = 0
491-
}
465+
if (pendingFull || mutationBurstCount >= MUTATION_BURST_THRESHOLD) {
466+
triggerMutationCooldown(now)
492467
return
493468
}
494469
if (state.mutationCount >= MAX_MUTATION_COUNT) {

0 commit comments

Comments
 (0)