feat(runtime-core): config.performance tracing support
This commit is contained in:
parent
a022b63605
commit
e93e426bfa
@ -18,7 +18,8 @@ module.exports = {
|
|||||||
'packages/*/src/**/*.ts',
|
'packages/*/src/**/*.ts',
|
||||||
'!packages/runtime-test/src/utils/**',
|
'!packages/runtime-test/src/utils/**',
|
||||||
'!packages/template-explorer/**',
|
'!packages/template-explorer/**',
|
||||||
'!packages/size-check/**'
|
'!packages/size-check/**',
|
||||||
|
'!packages/runtime-core/src/profiling.ts'
|
||||||
],
|
],
|
||||||
watchPathIgnorePatterns: ['/node_modules/', '/dist/', '/.git/'],
|
watchPathIgnorePatterns: ['/node_modules/', '/dist/', '/.git/'],
|
||||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
||||||
|
@ -41,6 +41,7 @@ import {
|
|||||||
currentRenderingInstance,
|
currentRenderingInstance,
|
||||||
markAttrsAccessed
|
markAttrsAccessed
|
||||||
} from './componentRenderUtils'
|
} from './componentRenderUtils'
|
||||||
|
import { startMeasure, endMeasure } from './profiling'
|
||||||
|
|
||||||
export type Data = { [key: string]: unknown }
|
export type Data = { [key: string]: unknown }
|
||||||
|
|
||||||
@ -108,6 +109,7 @@ export type RenderFunction = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ComponentInternalInstance {
|
export interface ComponentInternalInstance {
|
||||||
|
uid: number
|
||||||
type: Component
|
type: Component
|
||||||
parent: ComponentInternalInstance | null
|
parent: ComponentInternalInstance | null
|
||||||
appContext: AppContext
|
appContext: AppContext
|
||||||
@ -176,6 +178,8 @@ export interface ComponentInternalInstance {
|
|||||||
|
|
||||||
const emptyAppContext = createAppContext()
|
const emptyAppContext = createAppContext()
|
||||||
|
|
||||||
|
let uid = 0
|
||||||
|
|
||||||
export function createComponentInstance(
|
export function createComponentInstance(
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
parent: ComponentInternalInstance | null,
|
parent: ComponentInternalInstance | null,
|
||||||
@ -185,6 +189,7 @@ export function createComponentInstance(
|
|||||||
const appContext =
|
const appContext =
|
||||||
(parent ? parent.appContext : vnode.appContext) || emptyAppContext
|
(parent ? parent.appContext : vnode.appContext) || emptyAppContext
|
||||||
const instance: ComponentInternalInstance = {
|
const instance: ComponentInternalInstance = {
|
||||||
|
uid: uid++,
|
||||||
vnode,
|
vnode,
|
||||||
parent,
|
parent,
|
||||||
appContext,
|
appContext,
|
||||||
@ -383,7 +388,7 @@ function setupStatefulComponent(
|
|||||||
handleSetupResult(instance, setupResult, parentSuspense, isSSR)
|
handleSetupResult(instance, setupResult, parentSuspense, isSSR)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
finishComponentSetup(instance, parentSuspense, isSSR)
|
finishComponentSetup(instance, isSSR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +418,7 @@ export function handleSetupResult(
|
|||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
finishComponentSetup(instance, parentSuspense, isSSR)
|
finishComponentSetup(instance, isSSR)
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompileFunction = (
|
type CompileFunction = (
|
||||||
@ -430,7 +435,6 @@ export function registerRuntimeCompiler(_compile: any) {
|
|||||||
|
|
||||||
function finishComponentSetup(
|
function finishComponentSetup(
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
|
||||||
isSSR: boolean
|
isSSR: boolean
|
||||||
) {
|
) {
|
||||||
const Component = instance.type as ComponentOptions
|
const Component = instance.type as ComponentOptions
|
||||||
@ -442,9 +446,15 @@ function finishComponentSetup(
|
|||||||
}
|
}
|
||||||
} else if (!instance.render) {
|
} else if (!instance.render) {
|
||||||
if (compile && Component.template && !Component.render) {
|
if (compile && Component.template && !Component.render) {
|
||||||
|
if (__DEV__) {
|
||||||
|
startMeasure(instance, `compile`)
|
||||||
|
}
|
||||||
Component.render = compile(Component.template, {
|
Component.render = compile(Component.template, {
|
||||||
isCustomElement: instance.appContext.config.isCustomElement || NO
|
isCustomElement: instance.appContext.config.isCustomElement || NO
|
||||||
})
|
})
|
||||||
|
if (__DEV__ && instance.appContext.config.performance) {
|
||||||
|
endMeasure(instance, `compile`)
|
||||||
|
}
|
||||||
// mark the function as runtime compiled
|
// mark the function as runtime compiled
|
||||||
;(Component.render as RenderFunction)._rc = true
|
;(Component.render as RenderFunction)._rc = true
|
||||||
}
|
}
|
||||||
@ -529,3 +539,23 @@ export function recordInstanceBoundEffect(effect: ReactiveEffect) {
|
|||||||
;(currentInstance.effects || (currentInstance.effects = [])).push(effect)
|
;(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'
|
||||||
|
}
|
||||||
|
42
packages/runtime-core/src/profiling.ts
Normal file
42
packages/runtime-core/src/profiling.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
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
|
||||||
|
}
|
@ -68,6 +68,7 @@ import {
|
|||||||
} from './errorHandling'
|
} from './errorHandling'
|
||||||
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
|
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
|
||||||
import { invokeDirectiveHook } from './directives'
|
import { invokeDirectiveHook } from './directives'
|
||||||
|
import { startMeasure, endMeasure } from './profiling'
|
||||||
|
|
||||||
const __HMR__ = __BUNDLER__ && __DEV__
|
const __HMR__ = __BUNDLER__ && __DEV__
|
||||||
|
|
||||||
@ -1031,6 +1032,7 @@ function baseCreateRenderer(
|
|||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
pushWarningContext(initialVNode)
|
pushWarningContext(initialVNode)
|
||||||
|
startMeasure(instance, `mount`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// inject renderer internals for keepAlive
|
// inject renderer internals for keepAlive
|
||||||
@ -1041,7 +1043,13 @@ function baseCreateRenderer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// resolve props and slots for setup context
|
// resolve props and slots for setup context
|
||||||
|
if (__DEV__) {
|
||||||
|
startMeasure(instance, `init`)
|
||||||
|
}
|
||||||
setupComponent(instance, parentSuspense)
|
setupComponent(instance, parentSuspense)
|
||||||
|
if (__DEV__) {
|
||||||
|
endMeasure(instance, `init`)
|
||||||
|
}
|
||||||
|
|
||||||
// setup() is async. This component relies on async logic to be resolved
|
// setup() is async. This component relies on async logic to be resolved
|
||||||
// before proceeding
|
// before proceeding
|
||||||
@ -1072,6 +1080,7 @@ function baseCreateRenderer(
|
|||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
popWarningContext()
|
popWarningContext()
|
||||||
|
endMeasure(instance, `mount`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1089,7 +1098,13 @@ function baseCreateRenderer(
|
|||||||
let vnodeHook: VNodeHook | null | undefined
|
let vnodeHook: VNodeHook | null | undefined
|
||||||
const { el, props } = initialVNode
|
const { el, props } = initialVNode
|
||||||
const { bm, m, a, parent } = instance
|
const { bm, m, a, parent } = instance
|
||||||
|
if (__DEV__) {
|
||||||
|
startMeasure(instance, `render`)
|
||||||
|
}
|
||||||
const subTree = (instance.subTree = renderComponentRoot(instance))
|
const subTree = (instance.subTree = renderComponentRoot(instance))
|
||||||
|
if (__DEV__) {
|
||||||
|
endMeasure(instance, `render`)
|
||||||
|
}
|
||||||
// beforeMount hook
|
// beforeMount hook
|
||||||
if (bm) {
|
if (bm) {
|
||||||
invokeHooks(bm)
|
invokeHooks(bm)
|
||||||
@ -1099,6 +1114,9 @@ function baseCreateRenderer(
|
|||||||
invokeVNodeHook(vnodeHook, parent, initialVNode)
|
invokeVNodeHook(vnodeHook, parent, initialVNode)
|
||||||
}
|
}
|
||||||
if (el && hydrateNode) {
|
if (el && hydrateNode) {
|
||||||
|
if (__DEV__) {
|
||||||
|
startMeasure(instance, `hydrate`)
|
||||||
|
}
|
||||||
// vnode has adopted host node - perform hydration instead of mount.
|
// vnode has adopted host node - perform hydration instead of mount.
|
||||||
hydrateNode(
|
hydrateNode(
|
||||||
initialVNode.el as Node,
|
initialVNode.el as Node,
|
||||||
@ -1106,7 +1124,13 @@ function baseCreateRenderer(
|
|||||||
instance,
|
instance,
|
||||||
parentSuspense
|
parentSuspense
|
||||||
)
|
)
|
||||||
|
if (__DEV__) {
|
||||||
|
endMeasure(instance, `hydrate`)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (__DEV__) {
|
||||||
|
startMeasure(instance, `patch`)
|
||||||
|
}
|
||||||
patch(
|
patch(
|
||||||
null,
|
null,
|
||||||
subTree,
|
subTree,
|
||||||
@ -1116,6 +1140,9 @@ function baseCreateRenderer(
|
|||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG
|
isSVG
|
||||||
)
|
)
|
||||||
|
if (__DEV__) {
|
||||||
|
endMeasure(instance, `patch`)
|
||||||
|
}
|
||||||
initialVNode.el = subTree.el
|
initialVNode.el = subTree.el
|
||||||
}
|
}
|
||||||
// mounted hook
|
// mounted hook
|
||||||
@ -1151,7 +1178,13 @@ function baseCreateRenderer(
|
|||||||
} else {
|
} else {
|
||||||
next = vnode
|
next = vnode
|
||||||
}
|
}
|
||||||
|
if (__DEV__) {
|
||||||
|
startMeasure(instance, `render`)
|
||||||
|
}
|
||||||
const nextTree = renderComponentRoot(instance)
|
const nextTree = renderComponentRoot(instance)
|
||||||
|
if (__DEV__) {
|
||||||
|
endMeasure(instance, `render`)
|
||||||
|
}
|
||||||
const prevTree = instance.subTree
|
const prevTree = instance.subTree
|
||||||
instance.subTree = nextTree
|
instance.subTree = nextTree
|
||||||
next.el = vnode.el
|
next.el = vnode.el
|
||||||
@ -1168,6 +1201,9 @@ function baseCreateRenderer(
|
|||||||
if (instance.refs !== EMPTY_OBJ) {
|
if (instance.refs !== EMPTY_OBJ) {
|
||||||
instance.refs = {}
|
instance.refs = {}
|
||||||
}
|
}
|
||||||
|
if (__DEV__) {
|
||||||
|
startMeasure(instance, `patch`)
|
||||||
|
}
|
||||||
patch(
|
patch(
|
||||||
prevTree,
|
prevTree,
|
||||||
nextTree,
|
nextTree,
|
||||||
@ -1179,6 +1215,9 @@ function baseCreateRenderer(
|
|||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG
|
isSVG
|
||||||
)
|
)
|
||||||
|
if (__DEV__) {
|
||||||
|
endMeasure(instance, `patch`)
|
||||||
|
}
|
||||||
next.el = nextTree.el
|
next.el = nextTree.el
|
||||||
if (next === null) {
|
if (next === null) {
|
||||||
// self-triggered update. In case of HOC, update parent component
|
// self-triggered update. In case of HOC, update parent component
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import { VNode } from './vnode'
|
import { VNode } from './vnode'
|
||||||
import { Data, ComponentInternalInstance, Component } from './component'
|
import {
|
||||||
|
Data,
|
||||||
|
ComponentInternalInstance,
|
||||||
|
Component,
|
||||||
|
formatComponentName
|
||||||
|
} from './component'
|
||||||
import { isString, isFunction } from '@vue/shared'
|
import { isString, isFunction } from '@vue/shared'
|
||||||
import { toRaw, isRef, pauseTracking, resetTracking } from '@vue/reactivity'
|
import { toRaw, isRef, pauseTracking, resetTracking } from '@vue/reactivity'
|
||||||
import { callWithErrorHandling, ErrorCodes } from './errorHandling'
|
import { callWithErrorHandling, ErrorCodes } from './errorHandling'
|
||||||
@ -43,7 +48,10 @@ export function warn(msg: string, ...args: any[]) {
|
|||||||
msg + args.join(''),
|
msg + args.join(''),
|
||||||
instance && instance.proxy,
|
instance && instance.proxy,
|
||||||
trace
|
trace
|
||||||
.map(({ vnode }) => `at <${formatComponentName(vnode)}>`)
|
.map(
|
||||||
|
({ vnode }) =>
|
||||||
|
`at <${formatComponentName(vnode.type as Component)}>`
|
||||||
|
)
|
||||||
.join('\n'),
|
.join('\n'),
|
||||||
trace
|
trace
|
||||||
]
|
]
|
||||||
@ -111,24 +119,6 @@ function formatTraceEntry({ vnode, recurseCount }: TraceEntry): any[] {
|
|||||||
: [open + close, rootLabel]
|
: [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[] {
|
function formatProps(props: Data): any[] {
|
||||||
const res: any[] = []
|
const res: any[] = []
|
||||||
const keys = Object.keys(props)
|
const keys = Object.keys(props)
|
||||||
|
Loading…
Reference in New Issue
Block a user