'packages/*/src/**/*.ts',
'!packages/runtime-test/src/utils/**',
'!packages/template-explorer/**',
- '!packages/size-check/**'
+ '!packages/size-check/**',
+ '!packages/runtime-core/src/profiling.ts'
],
watchPathIgnorePatterns: ['/node_modules/', '/dist/', '/.git/'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
currentRenderingInstance,
markAttrsAccessed
} from './componentRenderUtils'
+import { startMeasure, endMeasure } from './profiling'
export type Data = { [key: string]: unknown }
}
export interface ComponentInternalInstance {
+ uid: number
type: Component
parent: ComponentInternalInstance | null
appContext: AppContext
const emptyAppContext = createAppContext()
+let uid = 0
+
export function createComponentInstance(
vnode: VNode,
parent: ComponentInternalInstance | null,
const appContext =
(parent ? parent.appContext : vnode.appContext) || emptyAppContext
const instance: ComponentInternalInstance = {
+ uid: uid++,
vnode,
parent,
appContext,
handleSetupResult(instance, setupResult, parentSuspense, isSSR)
}
} else {
- finishComponentSetup(instance, parentSuspense, isSSR)
+ finishComponentSetup(instance, isSSR)
}
}
}`
)
}
- finishComponentSetup(instance, parentSuspense, isSSR)
+ finishComponentSetup(instance, isSSR)
}
type CompileFunction = (
function finishComponentSetup(
instance: ComponentInternalInstance,
- parentSuspense: SuspenseBoundary | null,
isSSR: boolean
) {
const Component = instance.type as ComponentOptions
}
} else if (!instance.render) {
if (compile && Component.template && !Component.render) {
+ if (__DEV__) {
+ startMeasure(instance, `compile`)
+ }
Component.render = compile(Component.template, {
isCustomElement: instance.appContext.config.isCustomElement || NO
})
+ if (__DEV__ && instance.appContext.config.performance) {
+ endMeasure(instance, `compile`)
+ }
// mark the function as runtime compiled
;(Component.render as RenderFunction)._rc = true
}
;(currentInstance.effects || (currentInstance.effects = [])).push(effect)
}
}
+
+const classifyRE = /(?:^|[-_])(\w)/g
+const classify = (str: string): string =>
+ str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
+
+export function formatComponentName(
+ Component: Component,
+ file?: string
+): string {
+ let name = isFunction(Component)
+ ? Component.displayName || Component.name
+ : Component.name
+ if (!name && file) {
+ const match = file.match(/([^/\\]+)\.vue$/)
+ if (match) {
+ name = match[1]
+ }
+ }
+ return name ? classify(name) : 'Anonymous'
+}
--- /dev/null
+import { ComponentInternalInstance, formatComponentName } from './component'
+
+let supported: boolean
+let perf: any
+
+export function startMeasure(
+ instance: ComponentInternalInstance,
+ type: string
+) {
+ if (!instance.appContext) debugger
+ if (instance.appContext.config.performance && isSupported()) {
+ perf.mark(`vue-${type}-${instance.uid}`)
+ }
+}
+
+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.type)}> ${type}`,
+ startTag,
+ endTag
+ )
+ perf.clearMarks(startTag)
+ perf.clearMarks(endTag)
+ }
+}
+
+function isSupported() {
+ if (supported !== undefined) {
+ return supported
+ }
+ if (typeof window !== 'undefined' && window.performance) {
+ supported = true
+ perf = window.performance
+ } else {
+ supported = false
+ }
+ return supported
+}
} from './errorHandling'
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
import { invokeDirectiveHook } from './directives'
+import { startMeasure, endMeasure } from './profiling'
const __HMR__ = __BUNDLER__ && __DEV__
if (__DEV__) {
pushWarningContext(initialVNode)
+ startMeasure(instance, `mount`)
}
// inject renderer internals for keepAlive
}
// resolve props and slots for setup context
+ if (__DEV__) {
+ startMeasure(instance, `init`)
+ }
setupComponent(instance, parentSuspense)
+ if (__DEV__) {
+ endMeasure(instance, `init`)
+ }
// setup() is async. This component relies on async logic to be resolved
// before proceeding
if (__DEV__) {
popWarningContext()
+ endMeasure(instance, `mount`)
}
}
let vnodeHook: VNodeHook | null | undefined
const { el, props } = initialVNode
const { bm, m, a, parent } = instance
+ if (__DEV__) {
+ startMeasure(instance, `render`)
+ }
const subTree = (instance.subTree = renderComponentRoot(instance))
+ if (__DEV__) {
+ endMeasure(instance, `render`)
+ }
// beforeMount hook
if (bm) {
invokeHooks(bm)
invokeVNodeHook(vnodeHook, parent, initialVNode)
}
if (el && hydrateNode) {
+ if (__DEV__) {
+ startMeasure(instance, `hydrate`)
+ }
// vnode has adopted host node - perform hydration instead of mount.
hydrateNode(
initialVNode.el as Node,
instance,
parentSuspense
)
+ if (__DEV__) {
+ endMeasure(instance, `hydrate`)
+ }
} else {
+ if (__DEV__) {
+ startMeasure(instance, `patch`)
+ }
patch(
null,
subTree,
parentSuspense,
isSVG
)
+ if (__DEV__) {
+ endMeasure(instance, `patch`)
+ }
initialVNode.el = subTree.el
}
// mounted hook
} else {
next = vnode
}
+ if (__DEV__) {
+ startMeasure(instance, `render`)
+ }
const nextTree = renderComponentRoot(instance)
+ if (__DEV__) {
+ endMeasure(instance, `render`)
+ }
const prevTree = instance.subTree
instance.subTree = nextTree
next.el = vnode.el
if (instance.refs !== EMPTY_OBJ) {
instance.refs = {}
}
+ if (__DEV__) {
+ startMeasure(instance, `patch`)
+ }
patch(
prevTree,
nextTree,
parentSuspense,
isSVG
)
+ if (__DEV__) {
+ endMeasure(instance, `patch`)
+ }
next.el = nextTree.el
if (next === null) {
// self-triggered update. In case of HOC, update parent component
import { VNode } from './vnode'
-import { Data, ComponentInternalInstance, Component } from './component'
+import {
+ Data,
+ ComponentInternalInstance,
+ Component,
+ formatComponentName
+} from './component'
import { isString, isFunction } from '@vue/shared'
import { toRaw, isRef, pauseTracking, resetTracking } from '@vue/reactivity'
import { callWithErrorHandling, ErrorCodes } from './errorHandling'
msg + args.join(''),
instance && instance.proxy,
trace
- .map(({ vnode }) => `at <${formatComponentName(vnode)}>`)
+ .map(
+ ({ vnode }) =>
+ `at <${formatComponentName(vnode.type as Component)}>`
+ )
.join('\n'),
trace
]
: [open + close, rootLabel]
}
-const classifyRE = /(?:^|[-_])(\w)/g
-const classify = (str: string): string =>
- str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
-
-function formatComponentName(vnode: ComponentVNode, file?: string): string {
- const Component = vnode.type as Component
- let name = isFunction(Component)
- ? Component.displayName || Component.name
- : Component.name
- if (!name && file) {
- const match = file.match(/([^/\\]+)\.vue$/)
- if (match) {
- name = match[1]
- }
- }
- return name ? classify(name) : 'Anonymous'
-}
-
function formatProps(props: Data): any[] {
const res: any[] = []
const keys = Object.keys(props)