Skip to content

Commit 88e40a7

Browse files
committed
perf: throttle queue + subscribe simplification
1 parent d0b5fea commit 88e40a7

File tree

8 files changed

+208
-150
lines changed

8 files changed

+208
-150
lines changed

packages/app-backend-core/src/component.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { stringify, BridgeEvents, parse, SharedData } from '@vue-devtools/shared-utils'
1+
import { stringify, BridgeEvents, parse, SharedData, createThrottleQueue } from '@vue-devtools/shared-utils'
22
import { AppRecord, BackendContext, BuiltinBackendFeature } from '@vue-devtools/app-backend-api'
33
import { getAppRecord } from './app'
44
import { App, ComponentInstance, EditStatePayload } from '@vue/devtools-api'
@@ -131,11 +131,16 @@ export async function refreshComponentTreeSearch (ctx: BackendContext) {
131131
await sendComponentTreeData(ctx.currentAppRecord, '_root', ctx.currentAppRecord.componentFilter, null, false, ctx)
132132
}
133133

134-
export async function sendComponentUpdateTracking (instanceId: string, ctx: BackendContext) {
134+
const updateTrackingQueue = createThrottleQueue(500)
135+
136+
export function sendComponentUpdateTracking (instanceId: string, time: number, ctx: BackendContext) {
135137
if (!instanceId) return
136-
const payload = {
137-
instanceId,
138-
time: Date.now(), // Use normal date
139-
}
140-
ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_UPDATED, payload)
138+
139+
updateTrackingQueue.add(instanceId, () => {
140+
const payload = {
141+
instanceId,
142+
time,
143+
}
144+
ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_UPDATED, payload)
145+
})
141146
}

packages/app-backend-core/src/index.ts

+136-101
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
SharedData,
2020
isBrowser,
2121
raf,
22+
createThrottleQueue,
2223
} from '@vue-devtools/shared-utils'
2324
import debounce from 'lodash/debounce'
2425
import throttle from 'lodash/throttle'
@@ -118,27 +119,13 @@ async function connect () {
118119

119120
// Components
120121

121-
const sendComponentUpdate = throttle(async (appRecord: AppRecord, id: string) => {
122-
try {
123-
// Update component inspector
124-
if (ctx.currentInspectedComponentId === id) {
125-
await sendSelectedComponentData(appRecord, ctx.currentInspectedComponentId, ctx)
126-
}
127-
128-
// Update tree (tags)
129-
if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, sub => sub.payload.instanceId === id)) {
130-
await sendComponentTreeData(appRecord, id, appRecord.componentFilter, 0, false, ctx)
131-
}
132-
} catch (e) {
133-
if (SharedData.debugInfo) {
134-
console.error(e)
135-
}
136-
}
137-
}, 100)
122+
const throttleQueue = createThrottleQueue(500)
138123

139124
hook.on(HookEvents.COMPONENT_UPDATED, async (app, uid, parentUid, component) => {
140125
try {
141126
if (!app || (typeof uid !== 'number' && !uid) || !component) return
127+
const now = Date.now()
128+
142129
let id: string
143130
let appRecord: AppRecord
144131
if (app && uid != null) {
@@ -149,15 +136,40 @@ async function connect () {
149136
appRecord = ctx.currentAppRecord
150137
}
151138

152-
if (SharedData.trackUpdates) {
153-
await sendComponentUpdateTracking(id, ctx)
154-
}
139+
throttleQueue.add(`update:${id}`, async () => {
140+
try {
141+
const time = performance.now()
155142

156-
if (SharedData.flashUpdates) {
157-
await flashComponent(component, appRecord.backend)
158-
}
143+
const trackUpdateNow = performance.now()
144+
if (SharedData.trackUpdates) {
145+
sendComponentUpdateTracking(id, now, ctx)
146+
}
147+
const trackUpdateTime = performance.now() - trackUpdateNow
148+
149+
const flashNow = performance.now()
150+
if (SharedData.flashUpdates) {
151+
await flashComponent(component, appRecord.backend)
152+
}
153+
const flashTime = performance.now() - flashNow
159154

160-
await sendComponentUpdate(appRecord, id)
155+
const sendUpdateNow = performance.now()
156+
// Update component inspector
157+
if (ctx.currentInspectedComponentId === id) {
158+
await sendSelectedComponentData(appRecord, ctx.currentInspectedComponentId, ctx)
159+
}
160+
161+
// Update tree (tags)
162+
if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, id)) {
163+
await sendComponentTreeData(appRecord, id, appRecord.componentFilter, 0, false, ctx)
164+
}
165+
const sendUpdateTime = performance.now() - sendUpdateNow
166+
// console.log('COMPONENT_UPDATED', id, Math.round(performance.now() - time) + 'ms', { trackUpdateTime, flashTime, sendUpdateTime, updatedData: ctx.currentInspectedComponentId === id })
167+
} catch (e) {
168+
if (SharedData.debugInfo) {
169+
console.error(e)
170+
}
171+
}
172+
})
161173
} catch (e) {
162174
if (SharedData.debugInfo) {
163175
console.error(e)
@@ -168,51 +180,63 @@ async function connect () {
168180
hook.on(HookEvents.COMPONENT_ADDED, async (app, uid, parentUid, component) => {
169181
try {
170182
if (!app || (typeof uid !== 'number' && !uid) || !component) return
183+
const now = Date.now()
171184
const id = await getComponentId(app, uid, component, ctx)
172-
const appRecord = await getAppRecord(app, ctx)
173-
if (component) {
174-
if (component.__VUE_DEVTOOLS_UID__ == null) {
175-
component.__VUE_DEVTOOLS_UID__ = id
176-
}
177-
if (appRecord?.instanceMap) {
178-
if (!appRecord.instanceMap.has(id)) {
179-
appRecord.instanceMap.set(id, component)
180-
}
181-
}
182-
}
183185

184-
if (parentUid != null && appRecord?.instanceMap) {
185-
const parentInstances = await appRecord.backend.api.walkComponentParents(component)
186-
if (parentInstances.length) {
187-
// Check two parents level to update `hasChildren
188-
for (let i = 0; i < parentInstances.length; i++) {
189-
const parentId = await getComponentId(app, parentUid, parentInstances[i], ctx)
190-
if (i < 2 && isSubscribed(BridgeSubscriptions.COMPONENT_TREE, sub => sub.payload.instanceId === parentId)) {
191-
raf(() => {
192-
sendComponentTreeData(appRecord, parentId, appRecord.componentFilter, null, false, ctx)
193-
})
186+
throttleQueue.add(`add:${id}`, async () => {
187+
try {
188+
const time = performance.now()
189+
const appRecord = await getAppRecord(app, ctx)
190+
if (component) {
191+
if (component.__VUE_DEVTOOLS_UID__ == null) {
192+
component.__VUE_DEVTOOLS_UID__ = id
193+
}
194+
if (appRecord?.instanceMap) {
195+
if (!appRecord.instanceMap.has(id)) {
196+
appRecord.instanceMap.set(id, component)
197+
}
194198
}
199+
}
200+
201+
if (parentUid != null && appRecord?.instanceMap) {
202+
const parentInstances = await appRecord.backend.api.walkComponentParents(component)
203+
if (parentInstances.length) {
204+
// Check two parents level to update `hasChildren
205+
for (let i = 0; i < parentInstances.length; i++) {
206+
const parentId = await getComponentId(app, parentUid, parentInstances[i], ctx)
207+
if (i < 2 && isSubscribed(BridgeSubscriptions.COMPONENT_TREE, parentId)) {
208+
raf(() => {
209+
sendComponentTreeData(appRecord, parentId, appRecord.componentFilter, null, false, ctx)
210+
})
211+
}
195212

196-
if (SharedData.trackUpdates) {
197-
await sendComponentUpdateTracking(parentId, ctx)
213+
if (SharedData.trackUpdates) {
214+
sendComponentUpdateTracking(parentId, now, ctx)
215+
}
216+
}
198217
}
199218
}
200-
}
201-
}
202219

203-
if (ctx.currentInspectedComponentId === id) {
204-
await sendSelectedComponentData(appRecord, id, ctx)
205-
}
220+
if (ctx.currentInspectedComponentId === id) {
221+
await sendSelectedComponentData(appRecord, id, ctx)
222+
}
206223

207-
if (SharedData.trackUpdates) {
208-
await sendComponentUpdateTracking(id, ctx)
209-
}
224+
if (SharedData.trackUpdates) {
225+
sendComponentUpdateTracking(id, now, ctx)
226+
}
210227

211-
if (SharedData.flashUpdates) {
212-
await flashComponent(component, appRecord.backend)
213-
}
228+
if (SharedData.flashUpdates) {
229+
await flashComponent(component, appRecord.backend)
230+
}
214231

215-
await refreshComponentTreeSearch(ctx)
232+
await refreshComponentTreeSearch(ctx)
233+
// console.log('COMPONENT_ADDED', id, Math.round(performance.now() - time) + 'ms')
234+
} catch (e) {
235+
if (SharedData.debugInfo) {
236+
console.error(e)
237+
}
238+
}
239+
})
216240
} catch (e) {
217241
if (SharedData.debugInfo) {
218242
console.error(e)
@@ -223,39 +247,50 @@ async function connect () {
223247
hook.on(HookEvents.COMPONENT_REMOVED, async (app, uid, parentUid, component) => {
224248
try {
225249
if (!app || (typeof uid !== 'number' && !uid) || !component) return
226-
const appRecord = await getAppRecord(app, ctx)
227-
if (parentUid != null && appRecord) {
228-
const parentInstances = await appRecord.backend.api.walkComponentParents(component)
229-
if (parentInstances.length) {
230-
const parentId = await getComponentId(app, parentUid, parentInstances[0], ctx)
231-
if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, sub => sub.payload.instanceId === parentId)) {
232-
raf(async () => {
233-
try {
234-
const appRecord = await getAppRecord(app, ctx)
235-
236-
if (appRecord) {
237-
sendComponentTreeData(appRecord, parentId, appRecord.componentFilter, null, false, ctx)
238-
}
239-
} catch (e) {
240-
if (SharedData.debugInfo) {
241-
console.error(e)
242-
}
250+
const id = await getComponentId(app, uid, component, ctx)
251+
252+
throttleQueue.add(`remove:${id}`, async () => {
253+
try {
254+
const time = performance.now()
255+
const appRecord = await getAppRecord(app, ctx)
256+
if (parentUid != null && appRecord) {
257+
const parentInstances = await appRecord.backend.api.walkComponentParents(component)
258+
if (parentInstances.length) {
259+
const parentId = await getComponentId(app, parentUid, parentInstances[0], ctx)
260+
if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, parentId)) {
261+
raf(async () => {
262+
try {
263+
const appRecord = await getAppRecord(app, ctx)
264+
265+
if (appRecord) {
266+
sendComponentTreeData(appRecord, parentId, appRecord.componentFilter, null, false, ctx)
267+
}
268+
} catch (e) {
269+
if (SharedData.debugInfo) {
270+
console.error(e)
271+
}
272+
}
273+
})
243274
}
244-
})
275+
}
245276
}
246-
}
247-
}
248277

249-
const id = await getComponentId(app, uid, component, ctx)
250-
if (isSubscribed(BridgeSubscriptions.SELECTED_COMPONENT_DATA, sub => sub.payload.instanceId === id)) {
251-
await sendEmptyComponentData(id, ctx)
252-
}
278+
if (isSubscribed(BridgeSubscriptions.SELECTED_COMPONENT_DATA, id)) {
279+
await sendEmptyComponentData(id, ctx)
280+
}
253281

254-
if (appRecord) {
255-
appRecord.instanceMap.delete(id)
256-
}
282+
if (appRecord) {
283+
appRecord.instanceMap.delete(id)
284+
}
257285

258-
await refreshComponentTreeSearch(ctx)
286+
await refreshComponentTreeSearch(ctx)
287+
// console.log('COMPONENT_REMOVED', id, Math.round(performance.now() - time) + 'ms')
288+
} catch (e) {
289+
if (SharedData.debugInfo) {
290+
console.error(e)
291+
}
292+
}
293+
})
259294
} catch (e) {
260295
if (SharedData.debugInfo) {
261296
console.error(e)
@@ -264,7 +299,7 @@ async function connect () {
264299
})
265300

266301
hook.on(HookEvents.TRACK_UPDATE, (id, ctx) => {
267-
sendComponentUpdateTracking(id, ctx)
302+
sendComponentUpdateTracking(id, Date.now(), ctx)
268303
})
269304

270305
hook.on(HookEvents.FLASH_UPDATE, (instance, backend) => {
@@ -273,22 +308,22 @@ async function connect () {
273308

274309
// Component perf
275310

276-
hook.on(HookEvents.PERFORMANCE_START, async (app, uid, vm, type, time) => {
277-
await performanceMarkStart(app, uid, vm, type, time, ctx)
311+
hook.on(HookEvents.PERFORMANCE_START, (app, uid, vm, type, time) => {
312+
performanceMarkStart(app, uid, vm, type, time, ctx)
278313
})
279314

280-
hook.on(HookEvents.PERFORMANCE_END, async (app, uid, vm, type, time) => {
281-
await performanceMarkEnd(app, uid, vm, type, time, ctx)
315+
hook.on(HookEvents.PERFORMANCE_END, (app, uid, vm, type, time) => {
316+
performanceMarkEnd(app, uid, vm, type, time, ctx)
282317
})
283318

284319
// Highlighter
285320

286-
hook.on(HookEvents.COMPONENT_HIGHLIGHT, async instanceId => {
287-
await highlight(ctx.currentAppRecord.instanceMap.get(instanceId), ctx.currentAppRecord.backend, ctx)
321+
hook.on(HookEvents.COMPONENT_HIGHLIGHT, instanceId => {
322+
highlight(ctx.currentAppRecord.instanceMap.get(instanceId), ctx.currentAppRecord.backend, ctx)
288323
})
289324

290-
hook.on(HookEvents.COMPONENT_UNHIGHLIGHT, async () => {
291-
await unHighlight()
325+
hook.on(HookEvents.COMPONENT_UNHIGHLIGHT, () => {
326+
unHighlight()
292327
})
293328

294329
// Timeline
@@ -409,12 +444,12 @@ async function connect () {
409444
function connectBridge () {
410445
// Subscriptions
411446

412-
ctx.bridge.on(BridgeEvents.TO_BACK_SUBSCRIBE, ({ type, payload }) => {
413-
subscribe(type, payload)
447+
ctx.bridge.on(BridgeEvents.TO_BACK_SUBSCRIBE, ({ type, key }) => {
448+
subscribe(type, key)
414449
})
415450

416-
ctx.bridge.on(BridgeEvents.TO_BACK_UNSUBSCRIBE, ({ type, payload }) => {
417-
unsubscribe(type, payload)
451+
ctx.bridge.on(BridgeEvents.TO_BACK_UNSUBSCRIBE, ({ type, key }) => {
452+
unsubscribe(type, key)
418453
})
419454

420455
// Tabs
@@ -450,7 +485,7 @@ function connectBridge () {
450485

451486
ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_TREE, async ({ instanceId, filter, recursively }) => {
452487
ctx.currentAppRecord.componentFilter = filter
453-
subscribe(BridgeSubscriptions.COMPONENT_TREE, { instanceId })
488+
subscribe(BridgeSubscriptions.COMPONENT_TREE, instanceId)
454489
await sendComponentTreeData(ctx.currentAppRecord, instanceId, filter, null, recursively, ctx)
455490
})
456491

packages/app-backend-core/src/perf.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ export async function performanceMarkEnd (
145145
if (change) {
146146
// Update component tree
147147
const id = await getComponentId(app, uid, instance, ctx)
148-
if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, sub => sub.payload.instanceId === id)) {
148+
if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, id)) {
149149
raf(() => {
150150
sendComponentTreeData(appRecord, id, ctx.currentAppRecord.componentFilter, null, false, ctx)
151151
})

0 commit comments

Comments
 (0)