feat: Initial devtools support (#1125)

This commit is contained in:
Guillaume Chau
2020-07-17 00:18:52 +02:00
committed by GitHub
parent 5ed73cd874
commit 568b6db12b
15 changed files with 132 additions and 17 deletions

View File

@@ -13,6 +13,7 @@ import { isFunction, NO, isObject } from '@vue/shared'
import { warn } from './warning'
import { createVNode, cloneVNode, VNode } from './vnode'
import { RootHydrateFunction } from './hydration'
import { initApp, appUnmounted } from './devtools'
import { version } from '.'
export interface App<HostElement = any> {
@@ -31,7 +32,7 @@ export interface App<HostElement = any> {
unmount(rootContainer: HostElement | string): void
provide<T>(key: InjectionKey<T> | string, value: T): this
// internal. We need to expose these for the server-renderer
// internal. We need to expose these for the server-renderer and devtools
_component: Component
_props: Data | null
_container: HostElement | null
@@ -73,6 +74,9 @@ export interface AppContext {
directives: Record<string, Directive>
provides: Record<string | symbol, any>
reload?: () => void // HMR only
// internal for devtools
__app?: App
}
type PluginInstallFunction = (app: App, ...options: any[]) => any
@@ -226,6 +230,9 @@ export function createAppAPI<HostElement>(
}
isMounted = true
app._container = rootContainer
__DEV__ && initApp(app, version)
return vnode.component!.proxy
} else if (__DEV__) {
warn(
@@ -240,6 +247,8 @@ export function createAppAPI<HostElement>(
unmount() {
if (isMounted) {
render(null, app._container)
__DEV__ && appUnmounted(app)
} else if (__DEV__) {
warn(`Cannot unmount an app that is not mounted.`)
}
@@ -260,6 +269,8 @@ export function createAppAPI<HostElement>(
}
}
context.__app = app
return app
}
}

View File

@@ -49,6 +49,7 @@ import {
markAttrsAccessed
} from './componentRenderUtils'
import { startMeasure, endMeasure } from './profiling'
import { componentAdded } from './devtools'
export type Data = { [key: string]: unknown }
@@ -408,6 +409,9 @@ export function createComponentInstance(
}
instance.root = parent ? parent.root : instance
instance.emit = emit.bind(null, instance)
__DEV__ && componentAdded(instance)
return instance
}

View File

@@ -0,0 +1,78 @@
import { App } from './apiCreateApp'
import { Fragment, Text, Comment, Static } from './vnode'
import { ComponentInternalInstance } from './component'
export interface AppRecord {
id: number
app: App
version: string
types: { [key: string]: string | Symbol }
}
enum DevtoolsHooks {
APP_INIT = 'app:init',
APP_UNMOUNT = 'app:unmount',
COMPONENT_UPDATED = 'component:updated',
COMPONENT_ADDED = 'component:added',
COMPONENT_REMOVED = 'component:removed'
}
export interface DevtoolsHook {
emit: (event: string, ...payload: any[]) => void
on: (event: string, handler: Function) => void
once: (event: string, handler: Function) => void
off: (event: string, handler: Function) => void
appRecords: AppRecord[]
}
export let devtools: DevtoolsHook
export function setDevtoolsHook(hook: DevtoolsHook) {
devtools = hook
}
export function initApp(app: App, version: string) {
// TODO queue if devtools is undefined
if (!devtools) return
devtools.emit(DevtoolsHooks.APP_INIT, app, version, {
Fragment: Fragment,
Text: Text,
Comment: Comment,
Static: Static
})
}
export function appUnmounted(app: App) {
if (!devtools) return
devtools.emit(DevtoolsHooks.APP_UNMOUNT, app)
}
export function componentAdded(component: ComponentInternalInstance) {
if (!devtools || !component.appContext.__app) return
devtools.emit(
DevtoolsHooks.COMPONENT_ADDED,
component.appContext.__app,
component.uid,
component.parent ? component.parent.uid : undefined
)
}
export function componentUpdated(component: ComponentInternalInstance) {
if (!devtools || !component.appContext.__app) return
devtools.emit(
DevtoolsHooks.COMPONENT_UPDATED,
component.appContext.__app,
component.uid,
component.parent ? component.parent.uid : undefined
)
}
export function componentRemoved(component: ComponentInternalInstance) {
if (!devtools || !component.appContext.__app) return
devtools.emit(
DevtoolsHooks.COMPONENT_REMOVED,
component.appContext.__app,
component.uid,
component.parent ? component.parent.uid : undefined
)
}

View File

@@ -93,7 +93,10 @@ export {
getTransitionRawChildren
} from './components/BaseTransition'
// Types -----------------------------------------------------------------------
// For devtools
export { devtools, setDevtoolsHook } from './devtools'
// Types -------------------------------------------------------------------------
import { VNode } from './vnode'
import { ComponentInternalInstance } from './component'

View File

@@ -65,6 +65,7 @@ import { createHydrationFunctions, RootHydrateFunction } from './hydration'
import { invokeDirectiveHook } from './directives'
import { startMeasure, endMeasure } from './profiling'
import { ComponentPublicInstance } from './componentProxy'
import { componentRemoved, componentUpdated } from './devtools'
export interface Renderer<HostElement = RendererElement> {
render: RootRenderFunction<HostElement>
@@ -1417,6 +1418,7 @@ function baseCreateRenderer(
}
if (__DEV__) {
popWarningContext()
componentUpdated(instance)
}
}
}, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)
@@ -2068,6 +2070,8 @@ function baseCreateRenderer(
parentSuspense.resolve()
}
}
__DEV__ && componentRemoved(instance)
}
const unmountChildren: UnmountChildrenFn = (