| name | vue-devtools |
| description | Vue DevTools hook internals reference. Use when implementing or debugging Vue state observation — component tree walking, reactivity tracking, store integration, bug pattern detection. |
Vue DevTools Hook Internals
VUE_DEVTOOLS_GLOBAL_HOOK
Event emitter injected by DevTools extension before Vue loads.
interface VueDevToolsHook {
on(event: string, handler: Function)
once(event: string, handler: Function)
off(event: string, handler: Function)
emit(event: string, ...args: any[])
enabled: boolean
apps: Set<App>
appRecords: AppRecord[]
Buffer: any[]
}
Registration flow: Vue emits app:init on the hook during createApp().mount() → hook records the app → sets hook.enabled = true.
Key Events
| Event | Payload | When |
|---|
app:init | (app, version, types) | App mounts |
component:added | (app, uid, parentUid, instance) | Component created |
component:updated | (app, uid, parentUid, instance) | Component re-rendered |
component:removed | (app, uid, parentUid, instance) | Component destroyed |
component:emit | (app, instance, event, params) | $emit called |
Vue 3 Tree Walking
Components are linked via VNode subtrees:
function walkTree(instance) {
visit(instance)
const subTree = instance.subTree
if (subTree) walkVNode(subTree)
}
function walkVNode(vnode) {
if (vnode.component) {
walkTree(vnode.component)
}
if (Array.isArray(vnode.children)) {
vnode.children.forEach(child => {
if (typeof child === 'object') walkVNode(child)
})
}
}
Vue 2 Tree Walking
function walkTree(vm) {
visit(vm)
vm.$children.forEach(child => walkTree(child))
}
const instance = element.__vue__
const parent = vm.$parent
State Extraction
Vue 3
const state = {
props: instance.props,
setupState: instance.setupState,
data: instance.data,
computed:
provides: instance.provides,
}
const name = instance.type.name
|| instance.type.__name
|| instance.type.__file
Vue 2
const state = {
data: vm.$data,
props: vm.$props,
computed: Object.keys(vm._computedWatchers || {}),
watchers: vm._watchers,
}
const name = vm.$options.name || vm.$options._componentTag
Reactivity Debugging (Vue 3, dev-mode only)
import { watch, ref } from 'vue'
watch(someRef, (val) => { }, {
onTrack(e) {
},
onTrigger(e) {
},
})
For external observation without source modification, use watch() or effect() on exposed reactive state.
Store Access
Pinia
import { getActivePinia } from 'pinia'
const pinia = getActivePinia()
const stores = pinia._s
store.$subscribe((mutation, state) => {
})
store.$onAction(({ name, store, args, after, onError }) => {
after((result) => { })
onError((error) => { })
})
Vuex (Vue 2/3)
store.subscribe((mutation, state) => {
})
store.subscribeAction({
before(action, state) { },
after(action, state) { },
})
DOM Lookups
const app = element.__vue_app__
const instance = element.__vueParentComponent
const vm = element.__vue__
Buffer/Replay Mechanism
The hook buffers events for ~3 seconds before devtools connects. On connection:
hook.Buffer.forEach(({ event, args }) => {
processEvent(event, ...args)
})
hook.Buffer.length = 0
This means components mounted before devtools connects are still captured.
Performance Guidelines
- Lazy serialization — only serialize component state when inspector panel opens, not on every update
- Throttle updates — batch
component:updated events, process at most once per frame
- Bounded buffers — cap the event buffer to prevent memory leaks in long-running apps
- Skip internal components — filter out
<KeepAlive>, <Transition>, <RouterView> from tree displays unless requested