From 2476eaad6e9d68f0b75772456775a0a8165631c0 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 22 Sep 2021 09:07:08 -0400 Subject: [PATCH] fix(devtools): fix prod devtools detection + handle late devtools hook injection (#4653) --- .../__tests__/helpers/renderSlot.spec.ts | 2 +- packages/runtime-core/src/devtools.ts | 47 +++++++++++-------- packages/runtime-core/src/renderer.ts | 6 +-- packages/sfc-playground/src/main.ts | 5 ++ packages/sfc-playground/vite.config.ts | 3 +- 5 files changed, 39 insertions(+), 24 deletions(-) diff --git a/packages/runtime-core/__tests__/helpers/renderSlot.spec.ts b/packages/runtime-core/__tests__/helpers/renderSlot.spec.ts index 9af91ce1c03..4e661c406e6 100644 --- a/packages/runtime-core/__tests__/helpers/renderSlot.spec.ts +++ b/packages/runtime-core/__tests__/helpers/renderSlot.spec.ts @@ -47,7 +47,7 @@ describe('renderSlot', () => { return [createVNode('div', null, 'foo', PatchFlags.TEXT)] }, // mock instance - { type: {} } as any + { type: {}, appContext: {} } as any ) as Slot // manual invocation should not track diff --git a/packages/runtime-core/src/devtools.ts b/packages/runtime-core/src/devtools.ts index 4c52388524e..b26fae85d06 100644 --- a/packages/runtime-core/src/devtools.ts +++ b/packages/runtime-core/src/devtools.ts @@ -21,6 +21,7 @@ const enum DevtoolsHooks { } interface DevtoolsHook { + enabled?: boolean emit: (event: string, ...payload: any[]) => void on: (event: string, handler: Function) => void once: (event: string, handler: Function) => void @@ -30,14 +31,33 @@ interface DevtoolsHook { export let devtools: DevtoolsHook -export function setDevtoolsHook(hook: DevtoolsHook) { +let buffer: { event: string; args: any[] }[] = [] + +function emit(event: string, ...args: any[]) { + if (devtools) { + devtools.emit(event, ...args) + } else { + buffer.push({ event, args }) + } +} + +export function setDevtoolsHook(hook: DevtoolsHook, target: any) { devtools = hook + if (devtools) { + devtools.enabled = true + buffer.forEach(({ event, args }) => devtools.emit(event, ...args)) + buffer = [] + } else { + const replay = (target.__VUE_DEVTOOLS_HOOK_REPLAY__ = + target.__VUE_DEVTOOLS_HOOK_REPLAY__ || []) + replay.push((newHook: DevtoolsHook) => { + setDevtoolsHook(newHook, target) + }) + } } export function devtoolsInitApp(app: App, version: string) { - // TODO queue if devtools is undefined - if (!devtools) return - devtools.emit(DevtoolsHooks.APP_INIT, app, version, { + emit(DevtoolsHooks.APP_INIT, app, version, { Fragment, Text, Comment, @@ -46,8 +66,7 @@ export function devtoolsInitApp(app: App, version: string) { } export function devtoolsUnmountApp(app: App) { - if (!devtools) return - devtools.emit(DevtoolsHooks.APP_UNMOUNT, app) + emit(DevtoolsHooks.APP_UNMOUNT, app) } export const devtoolsComponentAdded = /*#__PURE__*/ createDevtoolsComponentHook( @@ -62,8 +81,7 @@ export const devtoolsComponentRemoved = function createDevtoolsComponentHook(hook: DevtoolsHooks) { return (component: ComponentInternalInstance) => { - if (!devtools) return - devtools.emit( + emit( hook, component.appContext.app, component.uid, @@ -83,15 +101,7 @@ export const devtoolsPerfEnd = /*#__PURE__*/ createDevtoolsPerformanceHook( function createDevtoolsPerformanceHook(hook: DevtoolsHooks) { return (component: ComponentInternalInstance, type: string, time: number) => { - if (!devtools) return - devtools.emit( - hook, - component.appContext.app, - component.uid, - component, - type, - time - ) + emit(hook, component.appContext.app, component.uid, component, type, time) } } @@ -100,8 +110,7 @@ export function devtoolsComponentEmit( event: string, params: any[] ) { - if (!devtools) return - devtools.emit( + emit( DevtoolsHooks.COMPONENT_EMIT, component.appContext.app, component, diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 76848cb52f3..e61e5421efc 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -340,10 +340,10 @@ function baseCreateRenderer( initFeatureFlags() } + const target = getGlobalThis() + target.__VUE__ = true if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { - const target = getGlobalThis() - target.__VUE__ = true - setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__) + setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__, target) } const { diff --git a/packages/sfc-playground/src/main.ts b/packages/sfc-playground/src/main.ts index 1ef69e7d28f..e645bb2bd0f 100644 --- a/packages/sfc-playground/src/main.ts +++ b/packages/sfc-playground/src/main.ts @@ -2,4 +2,9 @@ import { createApp } from 'vue' import App from './App.vue' import '@vue/repl/style.css' +// @ts-expect-error Custom window property +window.VUE_DEVTOOLS_CONFIG = { + defaultSelectedAppId: 'id:repl' +} + createApp(App).mount('#app') diff --git a/packages/sfc-playground/vite.config.ts b/packages/sfc-playground/vite.config.ts index 8fdff8ee8a2..6e5cdc450f6 100644 --- a/packages/sfc-playground/vite.config.ts +++ b/packages/sfc-playground/vite.config.ts @@ -9,7 +9,8 @@ const commit = execa.sync('git', ['rev-parse', 'HEAD']).stdout.slice(0, 7) export default defineConfig({ plugins: [vue(), copyVuePlugin()], define: { - __COMMIT__: JSON.stringify(commit) + __COMMIT__: JSON.stringify(commit), + __VUE_PROD_DEVTOOLS__: JSON.stringify(true) }, optimizeDeps: { exclude: ['@vue/repl']