From 2f8838d08ca36bce58fa535adbaedbba1dcfab3f Mon Sep 17 00:00:00 2001
From: xiaodong2008 <admin@xiaodong.moe>
Date: Sun, 16 Jun 2024 14:10:45 +0800
Subject: [PATCH 1/6] feat(runtime-capor): add app.config.performance

---
 .../runtime-vapor/src/apiCreateVaporApp.ts    |   2 +
 packages/runtime-vapor/src/apiRender.ts       |  16 ++
 packages/runtime-vapor/src/component.ts       |  44 +++++
 packages/runtime-vapor/src/devtools.ts        | 162 ++++++++++++++++++
 packages/runtime-vapor/src/profiling.ts       |  54 ++++++
 5 files changed, 278 insertions(+)
 create mode 100644 packages/runtime-vapor/src/devtools.ts
 create mode 100644 packages/runtime-vapor/src/profiling.ts

diff --git a/packages/runtime-vapor/src/apiCreateVaporApp.ts b/packages/runtime-vapor/src/apiCreateVaporApp.ts
index 9d318c902..e3ffef020 100644
--- a/packages/runtime-vapor/src/apiCreateVaporApp.ts
+++ b/packages/runtime-vapor/src/apiCreateVaporApp.ts
@@ -172,6 +172,7 @@ export function createAppContext(): AppContext {
     app: null as any,
     config: {
       isNativeTag: NO,
+      performance: false,
       errorHandler: undefined,
       warnHandler: undefined,
       globalProperties: {},
@@ -227,6 +228,7 @@ export interface AppConfig {
   // @private
   readonly isNativeTag: (tag: string) => boolean
 
+  performance: boolean
   errorHandler?: (
     err: unknown,
     instance: ComponentInternalInstance | null,
diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts
index fcc900b8e..7e2d3e0b3 100644
--- a/packages/runtime-vapor/src/apiRender.ts
+++ b/packages/runtime-vapor/src/apiRender.ts
@@ -18,6 +18,7 @@ import {
 import { isArray, isFunction, isObject } from '@vue/shared'
 import { fallThroughAttrs } from './componentAttrs'
 import { VaporErrorCodes, callWithErrorHandling } from './errorHandling'
+import { endMeasure, startMeasure } from './profiling'
 
 export const fragmentKey = Symbol(__DEV__ ? `fragmentKey` : ``)
 
@@ -32,6 +33,9 @@ export function setupComponent(
   instance: ComponentInternalInstance,
   singleRoot: boolean = false,
 ): void {
+  if (__DEV__) {
+    startMeasure(instance, `init`)
+  }
   const reset = setCurrentInstance(instance)
   instance.scope.run(() => {
     const { component, props } = instance
@@ -93,6 +97,9 @@ export function setupComponent(
     return block
   })
   reset()
+  if (__DEV__) {
+    endMeasure(instance, `init`)
+  }
 }
 
 export function render(
@@ -118,6 +125,10 @@ function mountComponent(
   // hook: beforeMount
   invokeLifecycle(instance, VaporLifecycleHooks.BEFORE_MOUNT, 'beforeMount')
 
+  if (__DEV__) {
+    startMeasure(instance, 'mount')
+  }
+
   insert(instance.block!, instance.container)
 
   // hook: mounted
@@ -128,6 +139,11 @@ function mountComponent(
     instance => (instance.isMounted = true),
     true,
   )
+
+  if (__DEV__) {
+    endMeasure(instance, 'mount')
+  }
+
   return instance
 }
 
diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts
index 9a2517498..9603e86ca 100644
--- a/packages/runtime-vapor/src/component.ts
+++ b/packages/runtime-vapor/src/component.ts
@@ -414,6 +414,50 @@ function getAttrsProxy(instance: ComponentInternalInstance): Data {
   )
 }
 
+const classifyRE = /(?:^|[-_])(\w)/g
+const classify = (str: string): string =>
+  str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
+
+export function getComponentName(
+  Component: Component,
+  includeInferred = true,
+): string | false | undefined {
+  return isFunction(Component)
+    ? Component.displayName || Component.name
+    : Component.name || (includeInferred && Component.__name)
+}
+
+/* istanbul ignore next */
+export function formatComponentName(
+  instance: ComponentInternalInstance | null,
+  Component: Component,
+  isRoot = false,
+): string {
+  let name = getComponentName(Component)
+  if (!name && Component.__file) {
+    const match = Component.__file.match(/([^/\\]+)\.\w+$/)
+    if (match) {
+      name = match[1]
+    }
+  }
+
+  if (!name && instance && instance.parent) {
+    // try to infer the name based on reverse resolution
+    const inferFromRegistry = (registry: Record<string, any> | undefined) => {
+      for (const key in registry) {
+        if (registry[key] === Component) {
+          return key
+        }
+      }
+    }
+    name =
+      inferFromRegistry(instance.comps) ||
+      inferFromRegistry(instance.appContext.components)
+  }
+
+  return name ? classify(name) : isRoot ? `App` : `Anonymous`
+}
+
 /**
  * Dev-only
  */
diff --git a/packages/runtime-vapor/src/devtools.ts b/packages/runtime-vapor/src/devtools.ts
new file mode 100644
index 000000000..de912d61f
--- /dev/null
+++ b/packages/runtime-vapor/src/devtools.ts
@@ -0,0 +1,162 @@
+/* eslint-disable no-restricted-globals */
+import type { App } from './apiCreateVaporApp'
+import type { ComponentInternalInstance } from './component'
+
+interface AppRecord {
+  id: number
+  app: App
+  version: string
+  types: Record<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',
+  COMPONENT_EMIT = 'component:emit',
+  PERFORMANCE_START = 'perf:start',
+  PERFORMANCE_END = 'perf:end',
+}
+
+export interface DevtoolsHook {
+  enabled?: boolean
+  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[]
+  /**
+   * Added at https://github.com/vuejs/devtools/commit/f2ad51eea789006ab66942e5a27c0f0986a257f9
+   * Returns whether the arg was buffered or not
+   */
+  cleanupBuffer?: (matchArg: unknown) => boolean
+}
+
+export let devtools: DevtoolsHook
+
+let buffer: { event: string; args: any[] }[] = []
+
+let devtoolsNotInstalled = false
+
+function emit(event: string, ...args: any[]) {
+  if (devtools) {
+    devtools.emit(event, ...args)
+  } else if (!devtoolsNotInstalled) {
+    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 if (
+    // handle late devtools injection - only do this if we are in an actual
+    // browser environment to avoid the timer handle stalling test runner exit
+    // (#4815)
+    typeof window !== 'undefined' &&
+    // some envs mock window but not fully
+    window.HTMLElement // &&
+    // also exclude jsdom
+    // !window.navigator?.userAgent?.includes('jsdom')
+  ) {
+    const replay = (target.__VUE_DEVTOOLS_HOOK_REPLAY__ =
+      target.__VUE_DEVTOOLS_HOOK_REPLAY__ || [])
+    replay.push((newHook: DevtoolsHook) => {
+      setDevtoolsHook(newHook, target)
+    })
+    // clear buffer after 3s - the user probably doesn't have devtools installed
+    // at all, and keeping the buffer will cause memory leaks (#4738)
+    setTimeout(() => {
+      if (!devtools) {
+        target.__VUE_DEVTOOLS_HOOK_REPLAY__ = null
+        devtoolsNotInstalled = true
+        buffer = []
+      }
+    }, 3000)
+  } else {
+    // non-browser env, assume not installed
+    devtoolsNotInstalled = true
+    buffer = []
+  }
+}
+
+export function devtoolsInitApp(app: App, version: string) {
+  emit(DevtoolsHooks.APP_INIT, app, version, {
+    Text,
+    Comment,
+  })
+}
+
+export function devtoolsUnmountApp(app: App) {
+  emit(DevtoolsHooks.APP_UNMOUNT, app)
+}
+
+export const devtoolsComponentAdded = /*#__PURE__*/ createDevtoolsComponentHook(
+  DevtoolsHooks.COMPONENT_ADDED,
+)
+
+export const devtoolsComponentUpdated =
+  /*#__PURE__*/ createDevtoolsComponentHook(DevtoolsHooks.COMPONENT_UPDATED)
+
+const _devtoolsComponentRemoved = /*#__PURE__*/ createDevtoolsComponentHook(
+  DevtoolsHooks.COMPONENT_REMOVED,
+)
+
+export const devtoolsComponentRemoved = (
+  component: ComponentInternalInstance,
+) => {
+  if (
+    devtools &&
+    typeof devtools.cleanupBuffer === 'function' &&
+    // remove the component if it wasn't buffered
+    !devtools.cleanupBuffer(component)
+  ) {
+    _devtoolsComponentRemoved(component)
+  }
+}
+
+/*! #__NO_SIDE_EFFECTS__ */
+function createDevtoolsComponentHook(hook: DevtoolsHooks) {
+  return (component: ComponentInternalInstance) => {
+    emit(
+      hook,
+      component.appContext.app,
+      component.uid,
+      component.parent ? component.parent.uid : undefined,
+      component,
+    )
+  }
+}
+
+export const devtoolsPerfStart = /*#__PURE__*/ createDevtoolsPerformanceHook(
+  DevtoolsHooks.PERFORMANCE_START,
+)
+
+export const devtoolsPerfEnd = /*#__PURE__*/ createDevtoolsPerformanceHook(
+  DevtoolsHooks.PERFORMANCE_END,
+)
+
+function createDevtoolsPerformanceHook(hook: DevtoolsHooks) {
+  return (component: ComponentInternalInstance, type: string, time: number) => {
+    emit(hook, component.appContext.app, component.uid, component, type, time)
+  }
+}
+
+export function devtoolsComponentEmit(
+  component: ComponentInternalInstance,
+  event: string,
+  params: any[],
+) {
+  emit(
+    DevtoolsHooks.COMPONENT_EMIT,
+    component.appContext.app,
+    component,
+    event,
+    params,
+  )
+}
diff --git a/packages/runtime-vapor/src/profiling.ts b/packages/runtime-vapor/src/profiling.ts
new file mode 100644
index 000000000..3caae0a67
--- /dev/null
+++ b/packages/runtime-vapor/src/profiling.ts
@@ -0,0 +1,54 @@
+/* eslint-disable no-restricted-globals */
+import {
+  type ComponentInternalInstance,
+  formatComponentName,
+} from './component'
+import { devtoolsPerfEnd, devtoolsPerfStart } from './devtools'
+
+let supported: boolean
+let perf: Performance
+
+export function startMeasure(
+  instance: ComponentInternalInstance,
+  type: string,
+) {
+  if (instance.appContext.config.performance && isSupported()) {
+    perf.mark(`vue-${type}-${instance.uid}`)
+  }
+
+  if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
+    devtoolsPerfStart(instance, type, isSupported() ? perf.now() : Date.now())
+  }
+}
+
+export function endMeasure(instance: ComponentInternalInstance, type: string) {
+  if (instance.appContext.config.performance && isSupported()) {
+    const startTag = `vue-${type}-${instance.uid}`
+    const endTag = startTag + `:end`
+    perf.mark(endTag)
+    perf.measure(
+      `<${formatComponentName(instance, instance.component)}> ${type}`,
+      startTag,
+      endTag,
+    )
+    perf.clearMarks(startTag)
+    perf.clearMarks(endTag)
+  }
+
+  if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
+    devtoolsPerfEnd(instance, type, isSupported() ? perf.now() : Date.now())
+  }
+}
+
+function isSupported() {
+  if (supported !== undefined) {
+    return supported
+  }
+  if (typeof window !== 'undefined' && window.performance) {
+    supported = true
+    perf = window.performance
+  } else {
+    supported = false
+  }
+  return supported
+}

From 10e8628b070eee58408541cec97f023416149430 Mon Sep 17 00:00:00 2001
From: xiaodong2008 <admin@xiaodong.moe>
Date: Sun, 16 Jun 2024 14:23:19 +0800
Subject: [PATCH 2/6] refactor: move formatComponentName to component.ts

---
 packages/runtime-vapor/src/component.ts | 31 ++++++++--------
 packages/runtime-vapor/src/warning.ts   | 47 +------------------------
 2 files changed, 17 insertions(+), 61 deletions(-)

diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts
index 9603e86ca..a0f52ebef 100644
--- a/packages/runtime-vapor/src/component.ts
+++ b/packages/runtime-vapor/src/component.ts
@@ -413,7 +413,6 @@ function getAttrsProxy(instance: ComponentInternalInstance): Data {
     ))
   )
 }
-
 const classifyRE = /(?:^|[-_])(\w)/g
 const classify = (str: string): string =>
   str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
@@ -427,7 +426,6 @@ export function getComponentName(
     : Component.name || (includeInferred && Component.__name)
 }
 
-/* istanbul ignore next */
 export function formatComponentName(
   instance: ComponentInternalInstance | null,
   Component: Component,
@@ -441,19 +439,22 @@ export function formatComponentName(
     }
   }
 
-  if (!name && instance && instance.parent) {
-    // try to infer the name based on reverse resolution
-    const inferFromRegistry = (registry: Record<string, any> | undefined) => {
-      for (const key in registry) {
-        if (registry[key] === Component) {
-          return key
-        }
-      }
-    }
-    name =
-      inferFromRegistry(instance.comps) ||
-      inferFromRegistry(instance.appContext.components)
-  }
+  // TODO registry
+  // if (!name && instance && instance.parent) {
+  //   // try to infer the name based on reverse resolution
+  //   const inferFromRegistry = (registry: Record<string, any> | undefined) => {
+  //     for (const key in registry) {
+  //       if (registry[key] === Component) {
+  //         return key
+  //       }
+  //     }
+  //   }
+  //   name =
+  //     inferFromRegistry(
+  //       instance.components ||
+  //         (instance.parent.type as ComponentOptions).components,
+  //     ) || inferFromRegistry(instance.appContext.components)
+  // }
 
   return name ? classify(name) : isRoot ? `App` : `Anonymous`
 }
diff --git a/packages/runtime-vapor/src/warning.ts b/packages/runtime-vapor/src/warning.ts
index 5d7e6c51e..52fa70aa4 100644
--- a/packages/runtime-vapor/src/warning.ts
+++ b/packages/runtime-vapor/src/warning.ts
@@ -2,6 +2,7 @@ import {
   type Component,
   type ComponentInternalInstance,
   currentInstance,
+  formatComponentName,
 } from './component'
 import { isFunction, isString } from '@vue/shared'
 import { isRef, pauseTracking, resetTracking, toRaw } from '@vue/reactivity'
@@ -155,49 +156,3 @@ function formatProp(key: string, value: unknown, raw?: boolean): any {
     return raw ? value : [`${key}=`, value]
   }
 }
-
-export function getComponentName(
-  Component: Component,
-  includeInferred = true,
-): string | false | undefined {
-  return isFunction(Component)
-    ? Component.displayName || Component.name
-    : Component.name || (includeInferred && Component.__name)
-}
-
-export function formatComponentName(
-  instance: ComponentInternalInstance | null,
-  Component: Component,
-  isRoot = false,
-): string {
-  let name = getComponentName(Component)
-  if (!name && Component.__file) {
-    const match = Component.__file.match(/([^/\\]+)\.\w+$/)
-    if (match) {
-      name = match[1]
-    }
-  }
-
-  // TODO registry
-  // if (!name && instance && instance.parent) {
-  //   // try to infer the name based on reverse resolution
-  //   const inferFromRegistry = (registry: Record<string, any> | undefined) => {
-  //     for (const key in registry) {
-  //       if (registry[key] === Component) {
-  //         return key
-  //       }
-  //     }
-  //   }
-  //   name =
-  //     inferFromRegistry(
-  //       instance.components ||
-  //         (instance.parent.type as ComponentOptions).components,
-  //     ) || inferFromRegistry(instance.appContext.components)
-  // }
-
-  return name ? classify(name) : isRoot ? `App` : `Anonymous`
-}
-
-const classifyRE = /(?:^|[-_])(\w)/g
-const classify = (str: string): string =>
-  str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')

From 78e316cf54ae507240d62a52e0bced1948f4b9ed Mon Sep 17 00:00:00 2001
From: xiaodong2008 <admin@xiaodong.moe>
Date: Sun, 16 Jun 2024 14:27:43 +0800
Subject: [PATCH 3/6] refactor: update import in warning.ts

---
 packages/runtime-vapor/src/warning.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/runtime-vapor/src/warning.ts b/packages/runtime-vapor/src/warning.ts
index 52fa70aa4..63ee30fed 100644
--- a/packages/runtime-vapor/src/warning.ts
+++ b/packages/runtime-vapor/src/warning.ts
@@ -1,5 +1,4 @@
 import {
-  type Component,
   type ComponentInternalInstance,
   currentInstance,
   formatComponentName,

From 54d8aca12a795666cca22ae8f78edfe33db048f5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?=
 <sxzz@sxzz.moe>
Date: Sun, 16 Jun 2024 16:39:51 +0800
Subject: [PATCH 4/6] fix

---
 packages/runtime-vapor/src/component.ts       | 27 ++++++++-----------
 .../src/helpers/resolveAssets.ts              |  2 +-
 2 files changed, 12 insertions(+), 17 deletions(-)

diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts
index a0f52ebef..e8ed1b892 100644
--- a/packages/runtime-vapor/src/component.ts
+++ b/packages/runtime-vapor/src/component.ts
@@ -439,22 +439,17 @@ export function formatComponentName(
     }
   }
 
-  // TODO registry
-  // if (!name && instance && instance.parent) {
-  //   // try to infer the name based on reverse resolution
-  //   const inferFromRegistry = (registry: Record<string, any> | undefined) => {
-  //     for (const key in registry) {
-  //       if (registry[key] === Component) {
-  //         return key
-  //       }
-  //     }
-  //   }
-  //   name =
-  //     inferFromRegistry(
-  //       instance.components ||
-  //         (instance.parent.type as ComponentOptions).components,
-  //     ) || inferFromRegistry(instance.appContext.components)
-  // }
+  if (!name && instance && instance.parent) {
+    // try to infer the name based on reverse resolution
+    const inferFromRegistry = (registry: Record<string, any> | undefined) => {
+      for (const key in registry) {
+        if (registry[key] === Component) {
+          return key
+        }
+      }
+    }
+    name = inferFromRegistry(instance.appContext.components)
+  }
 
   return name ? classify(name) : isRoot ? `App` : `Anonymous`
 }
diff --git a/packages/runtime-vapor/src/helpers/resolveAssets.ts b/packages/runtime-vapor/src/helpers/resolveAssets.ts
index 18eba78cb..5454de687 100644
--- a/packages/runtime-vapor/src/helpers/resolveAssets.ts
+++ b/packages/runtime-vapor/src/helpers/resolveAssets.ts
@@ -1,7 +1,7 @@
 import { camelize, capitalize } from '@vue/shared'
 import { type Directive, warn } from '..'
 import { type Component, currentInstance } from '../component'
-import { getComponentName } from '../warning'
+import { getComponentName } from '../component'
 
 export const COMPONENTS = 'components'
 export const DIRECTIVES = 'directives'

From 40b62f476fa17cd289c8f246855b3250a332935d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?=
 <sxzz@sxzz.moe>
Date: Sun, 16 Jun 2024 16:42:53 +0800
Subject: [PATCH 5/6] refactor

---
 packages/runtime-vapor/src/component.ts | 33 +++++++++++++------------
 packages/runtime-vapor/src/devtools.ts  | 10 +++-----
 2 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts
index e8ed1b892..228c7b78a 100644
--- a/packages/runtime-vapor/src/component.ts
+++ b/packages/runtime-vapor/src/component.ts
@@ -413,9 +413,20 @@ function getAttrsProxy(instance: ComponentInternalInstance): Data {
     ))
   )
 }
-const classifyRE = /(?:^|[-_])(\w)/g
-const classify = (str: string): string =>
-  str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
+
+/**
+ * Dev-only
+ */
+function getSlotsProxy(instance: ComponentInternalInstance): Slots {
+  return (
+    instance.slotsProxy ||
+    (instance.slotsProxy = new Proxy(instance.slots, {
+      get(target, key: string) {
+        return target[key]
+      },
+    }))
+  )
+}
 
 export function getComponentName(
   Component: Component,
@@ -454,16 +465,6 @@ export function formatComponentName(
   return name ? classify(name) : isRoot ? `App` : `Anonymous`
 }
 
-/**
- * Dev-only
- */
-function getSlotsProxy(instance: ComponentInternalInstance): Slots {
-  return (
-    instance.slotsProxy ||
-    (instance.slotsProxy = new Proxy(instance.slots, {
-      get(target, key: string) {
-        return target[key]
-      },
-    }))
-  )
-}
+const classifyRE = /(?:^|[-_])(\w)/g
+const classify = (str: string): string =>
+  str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
diff --git a/packages/runtime-vapor/src/devtools.ts b/packages/runtime-vapor/src/devtools.ts
index de912d61f..4dff1bd99 100644
--- a/packages/runtime-vapor/src/devtools.ts
+++ b/packages/runtime-vapor/src/devtools.ts
@@ -60,9 +60,10 @@ export function setDevtoolsHook(hook: DevtoolsHook, target: any) {
     // (#4815)
     typeof window !== 'undefined' &&
     // some envs mock window but not fully
-    window.HTMLElement // &&
+    window.HTMLElement &&
     // also exclude jsdom
-    // !window.navigator?.userAgent?.includes('jsdom')
+    // eslint-disable-next-line no-restricted-syntax
+    !window.navigator?.userAgent?.includes('jsdom')
   ) {
     const replay = (target.__VUE_DEVTOOLS_HOOK_REPLAY__ =
       target.__VUE_DEVTOOLS_HOOK_REPLAY__ || [])
@@ -86,10 +87,7 @@ export function setDevtoolsHook(hook: DevtoolsHook, target: any) {
 }
 
 export function devtoolsInitApp(app: App, version: string) {
-  emit(DevtoolsHooks.APP_INIT, app, version, {
-    Text,
-    Comment,
-  })
+  emit(DevtoolsHooks.APP_INIT, app, version, {})
 }
 
 export function devtoolsUnmountApp(app: App) {

From b193066080b56855c272cb317aa0918fa202bb96 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?=
 <sxzz@sxzz.moe>
Date: Sun, 16 Jun 2024 16:48:38 +0800
Subject: [PATCH 6/6] fix order

---
 packages/runtime-vapor/src/apiRender.ts | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts
index 7e2d3e0b3..a04e29833 100644
--- a/packages/runtime-vapor/src/apiRender.ts
+++ b/packages/runtime-vapor/src/apiRender.ts
@@ -122,13 +122,13 @@ function mountComponent(
 ) {
   instance.container = container
 
-  // hook: beforeMount
-  invokeLifecycle(instance, VaporLifecycleHooks.BEFORE_MOUNT, 'beforeMount')
-
   if (__DEV__) {
     startMeasure(instance, 'mount')
   }
 
+  // hook: beforeMount
+  invokeLifecycle(instance, VaporLifecycleHooks.BEFORE_MOUNT, 'beforeMount')
+
   insert(instance.block!, instance.container)
 
   // hook: mounted