From 6df53d85a207986128159d88565e6e7045db2add Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 16 Apr 2024 22:47:24 +0800 Subject: [PATCH] fix(runtime-core): use same internal object mechanism for slots close #10709 --- packages/runtime-core/src/componentProps.ts | 10 ++-------- packages/runtime-core/src/componentSlots.ts | 5 +++-- packages/runtime-core/src/internalObject.ts | 12 ++++++++++++ packages/runtime-core/src/vnode.ts | 8 +++----- 4 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 packages/runtime-core/src/internalObject.ts diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 1c87304185c..5a4292b6f36 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -38,6 +38,7 @@ import { createPropsDefaultThis } from './compat/props' import { isCompatEnabled, softAssertCompatEnabled } from './compat/compatConfig' import { DeprecationTypes } from './compat/compatConfig' import { shouldSkipAttr } from './compat/attrsFallthrough' +import { createInternalObject } from './internalObject' export type ComponentPropsOptions

= | ComponentObjectPropsOptions

@@ -185,13 +186,6 @@ 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, @@ -199,7 +193,7 @@ export function initProps( isSSR = false, ) { const props: Data = {} - const attrs: Data = Object.create(attrsProto) + const attrs: Data = createInternalObject() instance.propsDefaults = Object.create(null) diff --git a/packages/runtime-core/src/componentSlots.ts b/packages/runtime-core/src/componentSlots.ts index e0f051b3984..66a09e5fa1a 100644 --- a/packages/runtime-core/src/componentSlots.ts +++ b/packages/runtime-core/src/componentSlots.ts @@ -24,6 +24,7 @@ import { DeprecationTypes, isCompatEnabled } from './compat/compatConfig' import { toRaw } from '@vue/reactivity' import { trigger } from '@vue/reactivity' import { TriggerOpTypes } from '@vue/reactivity' +import { createInternalObject } from './internalObject' export type Slot = ( ...args: IfAny @@ -177,12 +178,12 @@ export const initSlots = ( } else { normalizeObjectSlots( children as RawSlots, - (instance.slots = {}), + (instance.slots = createInternalObject()), instance, ) } } else { - instance.slots = {} + instance.slots = createInternalObject() if (children) { normalizeVNodeSlots(instance, children) } diff --git a/packages/runtime-core/src/internalObject.ts b/packages/runtime-core/src/internalObject.ts new file mode 100644 index 00000000000..0c0c39bef6d --- /dev/null +++ b/packages/runtime-core/src/internalObject.ts @@ -0,0 +1,12 @@ +/** + * Used during vnode props/slots normalization to check if the vnode props/slots + * are the internal attrs / slots object of a component via + * `Object.getPrototypeOf`. This is more performant than defining a + * non-enumerable property. (one of the optimizations done for ssr-benchmark) + */ +const internalObjectProto = Object.create(null) + +export const createInternalObject = () => Object.create(internalObjectProto) + +export const isInternalObject = (obj: object) => + Object.getPrototypeOf(obj) === internalObjectProto diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index 28b60be78f2..a1a6a908d2a 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -55,7 +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' +import { isInternalObject } from './internalObject' export const Fragment = Symbol.for('v-fgt') as any as { __isFragment: true @@ -617,9 +617,7 @@ function _createVNode( export function guardReactiveProps(props: (Data & VNodeProps) | null) { if (!props) return null - return isProxy(props) || Object.getPrototypeOf(props) === attrsProto - ? extend({}, props) - : props + return isProxy(props) || isInternalObject(props) ? extend({}, props) : props } export function cloneVNode( @@ -791,7 +789,7 @@ export function normalizeChildren(vnode: VNode, children: unknown) { } else { type = ShapeFlags.SLOTS_CHILDREN const slotFlag = (children as RawSlots)._ - if (!slotFlag) { + if (!slotFlag && !isInternalObject(children)) { // if slots are not normalized, attach context instance // (compiled / normalized slots already have context) ;(children as RawSlots)._ctx = currentRenderingInstance