perf: optimize component props/slots internal object checks

This commit is contained in:
Evan You 2024-04-12 14:41:03 +08:00
parent 4bc9f39f02
commit 6af733d68e
3 changed files with 12 additions and 11 deletions

View File

@ -12,7 +12,6 @@ import {
PatchFlags, PatchFlags,
camelize, camelize,
capitalize, capitalize,
def,
extend, extend,
hasOwn, hasOwn,
hyphenate, hyphenate,
@ -34,7 +33,6 @@ import {
setCurrentInstance, setCurrentInstance,
} from './component' } from './component'
import { isEmitListener } from './componentEmits' import { isEmitListener } from './componentEmits'
import { InternalObjectKey } from './vnode'
import type { AppContext } from './apiCreateApp' import type { AppContext } from './apiCreateApp'
import { createPropsDefaultThis } from './compat/props' import { createPropsDefaultThis } from './compat/props'
import { isCompatEnabled, softAssertCompatEnabled } from './compat/compatConfig' import { isCompatEnabled, softAssertCompatEnabled } from './compat/compatConfig'
@ -187,6 +185,13 @@ type NormalizedProp =
export type NormalizedProps = Record<string, NormalizedProp> export type NormalizedProps = Record<string, NormalizedProp>
export type NormalizedPropsOptions = [NormalizedProps, string[]] | [] 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( export function initProps(
instance: ComponentInternalInstance, instance: ComponentInternalInstance,
rawProps: Data | null, rawProps: Data | null,
@ -194,8 +199,7 @@ export function initProps(
isSSR = false, isSSR = false,
) { ) {
const props: Data = {} const props: Data = {}
const attrs: Data = {} const attrs: Data = Object.create(attrsProto)
def(attrs, InternalObjectKey, 1)
instance.propsDefaults = Object.create(null) instance.propsDefaults = Object.create(null)

View File

@ -1,6 +1,5 @@
import { type ComponentInternalInstance, currentInstance } from './component' import { type ComponentInternalInstance, currentInstance } from './component'
import { import {
InternalObjectKey,
type VNode, type VNode,
type VNodeChild, type VNodeChild,
type VNodeNormalizedChildren, type VNodeNormalizedChildren,
@ -174,7 +173,7 @@ export const initSlots = (
// we should avoid the proxy object polluting the slots of the internal instance // we should avoid the proxy object polluting the slots of the internal instance
instance.slots = toRaw(children as InternalSlots) instance.slots = toRaw(children as InternalSlots)
// make compiler marker non-enumerable // make compiler marker non-enumerable
def(children as InternalSlots, '_', type) def(instance.slots, '_', type)
} else { } else {
normalizeObjectSlots( normalizeObjectSlots(
children as RawSlots, children as RawSlots,
@ -188,7 +187,6 @@ export const initSlots = (
normalizeVNodeSlots(instance, children) normalizeVNodeSlots(instance, children)
} }
} }
def(instance.slots, InternalObjectKey, 1)
} }
export const updateSlots = ( export const updateSlots = (

View File

@ -55,6 +55,7 @@ import { convertLegacyVModelProps } from './compat/componentVModel'
import { defineLegacyVNodeProperties } from './compat/renderFn' import { defineLegacyVNodeProperties } from './compat/renderFn'
import { ErrorCodes, callWithAsyncErrorHandling } from './errorHandling' import { ErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
import type { ComponentPublicInstance } from './componentPublicInstance' import type { ComponentPublicInstance } from './componentPublicInstance'
import { attrsProto } from './componentProps'
export const Fragment = Symbol.for('v-fgt') as any as { export const Fragment = Symbol.for('v-fgt') as any as {
__isFragment: true __isFragment: true
@ -404,8 +405,6 @@ const createVNodeWithArgsTransform = (
) )
} }
export const InternalObjectKey = `__vInternal`
const normalizeKey = ({ key }: VNodeProps): VNode['key'] => const normalizeKey = ({ key }: VNodeProps): VNode['key'] =>
key != null ? key : null key != null ? key : null
@ -618,7 +617,7 @@ function _createVNode(
export function guardReactiveProps(props: (Data & VNodeProps) | null) { export function guardReactiveProps(props: (Data & VNodeProps) | null) {
if (!props) return null if (!props) return null
return isProxy(props) || InternalObjectKey in props return isProxy(props) || Object.getPrototypeOf(props) === attrsProto
? extend({}, props) ? extend({}, props)
: props : props
} }
@ -792,7 +791,7 @@ export function normalizeChildren(vnode: VNode, children: unknown) {
} else { } else {
type = ShapeFlags.SLOTS_CHILDREN type = ShapeFlags.SLOTS_CHILDREN
const slotFlag = (children as RawSlots)._ const slotFlag = (children as RawSlots)._
if (!slotFlag && !(InternalObjectKey in children!)) { if (!slotFlag) {
// if slots are not normalized, attach context instance // if slots are not normalized, attach context instance
// (compiled / normalized slots already have context) // (compiled / normalized slots already have context)
;(children as RawSlots)._ctx = currentRenderingInstance ;(children as RawSlots)._ctx = currentRenderingInstance