From 6af733d68eb400a3d2c5ef5f465fff32b72a324e Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 12 Apr 2024 14:41:03 +0800 Subject: [PATCH] perf: optimize component props/slots internal object checks --- packages/runtime-core/src/componentProps.ts | 12 ++++++++---- packages/runtime-core/src/componentSlots.ts | 4 +--- packages/runtime-core/src/vnode.ts | 7 +++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 2d91affe082..c0cef2f0901 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -12,7 +12,6 @@ import { PatchFlags, camelize, capitalize, - def, extend, hasOwn, hyphenate, @@ -34,7 +33,6 @@ import { setCurrentInstance, } from './component' import { isEmitListener } from './componentEmits' -import { InternalObjectKey } from './vnode' import type { AppContext } from './apiCreateApp' import { createPropsDefaultThis } from './compat/props' import { isCompatEnabled, softAssertCompatEnabled } from './compat/compatConfig' @@ -187,6 +185,13 @@ type NormalizedProp = export type NormalizedProps = Record export type NormalizedPropsOptions = [NormalizedProps, string[]] | [] +/** + * Used during vnode props normalization to check if the vnode props is the + * attrs object of a component via `Object.getPrototypeOf`. This is more + * performant than defining a non-enumerable property. + */ +export const attrsProto = {} + export function initProps( instance: ComponentInternalInstance, rawProps: Data | null, @@ -194,8 +199,7 @@ export function initProps( isSSR = false, ) { const props: Data = {} - const attrs: Data = {} - def(attrs, InternalObjectKey, 1) + const attrs: Data = Object.create(attrsProto) instance.propsDefaults = Object.create(null) diff --git a/packages/runtime-core/src/componentSlots.ts b/packages/runtime-core/src/componentSlots.ts index 61e1ecc072c..e0f051b3984 100644 --- a/packages/runtime-core/src/componentSlots.ts +++ b/packages/runtime-core/src/componentSlots.ts @@ -1,6 +1,5 @@ import { type ComponentInternalInstance, currentInstance } from './component' import { - InternalObjectKey, type VNode, type VNodeChild, type VNodeNormalizedChildren, @@ -174,7 +173,7 @@ export const initSlots = ( // we should avoid the proxy object polluting the slots of the internal instance instance.slots = toRaw(children as InternalSlots) // make compiler marker non-enumerable - def(children as InternalSlots, '_', type) + def(instance.slots, '_', type) } else { normalizeObjectSlots( children as RawSlots, @@ -188,7 +187,6 @@ export const initSlots = ( normalizeVNodeSlots(instance, children) } } - def(instance.slots, InternalObjectKey, 1) } export const updateSlots = ( diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index daa9413d2ee..28b60be78f2 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -55,6 +55,7 @@ import { convertLegacyVModelProps } from './compat/componentVModel' import { defineLegacyVNodeProperties } from './compat/renderFn' import { ErrorCodes, callWithAsyncErrorHandling } from './errorHandling' import type { ComponentPublicInstance } from './componentPublicInstance' +import { attrsProto } from './componentProps' export const Fragment = Symbol.for('v-fgt') as any as { __isFragment: true @@ -404,8 +405,6 @@ const createVNodeWithArgsTransform = ( ) } -export const InternalObjectKey = `__vInternal` - const normalizeKey = ({ key }: VNodeProps): VNode['key'] => key != null ? key : null @@ -618,7 +617,7 @@ function _createVNode( export function guardReactiveProps(props: (Data & VNodeProps) | null) { if (!props) return null - return isProxy(props) || InternalObjectKey in props + return isProxy(props) || Object.getPrototypeOf(props) === attrsProto ? extend({}, props) : props } @@ -792,7 +791,7 @@ export function normalizeChildren(vnode: VNode, children: unknown) { } else { type = ShapeFlags.SLOTS_CHILDREN const slotFlag = (children as RawSlots)._ - if (!slotFlag && !(InternalObjectKey in children!)) { + if (!slotFlag) { // if slots are not normalized, attach context instance // (compiled / normalized slots already have context) ;(children as RawSlots)._ctx = currentRenderingInstance