Skip to content

perf: use new Empty() instead of Object.create(null) for performance #12172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/compiler-core/src/babelUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
ObjectProperty,
Program,
} from '@babel/types'
import { Empty } from '@vue/shared'
import { walk } from 'estree-walker'

/**
Expand All @@ -27,7 +28,7 @@ export function walkIdentifiers(
) => void,
includeAll = false,
parentStack: Node[] = [],
knownIds: Record<string, number> = Object.create(null),
knownIds: Record<string, number> = new Empty(),
): void {
if (__BROWSER__) {
return
Expand Down
3 changes: 2 additions & 1 deletion packages/compiler-core/src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from './ast'
import {
EMPTY_OBJ,
Empty,
NOOP,
PatchFlags,
camelize,
Expand Down Expand Up @@ -187,7 +188,7 @@ export function createTransformContext(
cached: [],
constantCache: new WeakMap(),
temps: 0,
identifiers: Object.create(null),
identifiers: new Empty(),
scopes: {
vFor: 0,
vSlot: 0,
Expand Down
6 changes: 3 additions & 3 deletions packages/compiler-sfc/src/compileScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
type SFCScriptBlock,
} from './parse'
import type { ParserPlugin } from '@babel/parser'
import { generateCodeFrame } from '@vue/shared'
import { Empty, generateCodeFrame } from '@vue/shared'
import type {
ArrayPattern,
CallExpression,
Expand Down Expand Up @@ -194,8 +194,8 @@ export function compileScript(

// metadata that needs to be returned
// const ctx.bindingMetadata: BindingMetadata = {}
const scriptBindings: Record<string, BindingTypes> = Object.create(null)
const setupBindings: Record<string, BindingTypes> = Object.create(null)
const scriptBindings: Record<string, BindingTypes> = new Empty()
const setupBindings: Record<string, BindingTypes> = new Empty()

let defaultExport: Node | undefined
let hasAwait = false
Expand Down
8 changes: 4 additions & 4 deletions packages/compiler-sfc/src/script/context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { CallExpression, Node, ObjectPattern, Program } from '@babel/types'
import type { SFCDescriptor } from '../parse'
import { generateCodeFrame, isArray } from '@vue/shared'
import { Empty, generateCodeFrame, isArray } from '@vue/shared'
import { type ParserPlugin, parse as babelParse } from '@babel/parser'
import type { ImportBinding, SFCScriptCompileOptions } from '../compileScript'
import type { PropsDestructureBindings } from './defineProps'
Expand Down Expand Up @@ -28,7 +28,7 @@ export class ScriptCompileContext {
// import / type analysis
scope?: TypeScope
globalScopes?: TypeScope[]
userImports: Record<string, ImportBinding> = Object.create(null)
userImports: Record<string, ImportBinding> = new Empty()

// macros presence check
hasDefinePropsCall = false
Expand All @@ -46,7 +46,7 @@ export class ScriptCompileContext {
propsRuntimeDecl: Node | undefined
propsTypeDecl: Node | undefined
propsDestructureDecl: ObjectPattern | undefined
propsDestructuredBindings: PropsDestructureBindings = Object.create(null)
propsDestructuredBindings: PropsDestructureBindings = new Empty()
propsDestructureRestId: string | undefined
propsRuntimeDefaults: Node | undefined

Expand All @@ -56,7 +56,7 @@ export class ScriptCompileContext {
emitDecl: Node | undefined

// defineModel
modelDecls: Record<string, ModelDecl> = Object.create(null)
modelDecls: Record<string, ModelDecl> = new Empty()

// defineOptions
optionsRuntimeDecl: Node | undefined
Expand Down
6 changes: 3 additions & 3 deletions packages/compiler-sfc/src/script/definePropsDestructure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
unwrapTSNode,
walkFunctionParams,
} from '@vue/compiler-dom'
import { genPropsAccessExp } from '@vue/shared'
import { Empty, genPropsAccessExp } from '@vue/shared'
import { isCallOf, resolveObjectKey } from './utils'
import type { ScriptCompileContext } from './context'
import { DEFINE_PROPS } from './defineProps'
Expand Down Expand Up @@ -103,12 +103,12 @@ export function transformDestructuredProps(
return
}

const rootScope: Scope = Object.create(null)
const rootScope: Scope = new Empty()
const scopeStack: Scope[] = [rootScope]
let currentScope: Scope = rootScope
const excludedIds = new WeakSet<Identifier>()
const parentStack: Node[] = []
const propsLocalToPublicMap: Record<string, string> = Object.create(null)
const propsLocalToPublicMap: Record<string, string> = new Empty()

for (const key in ctx.propsDestructuredBindings) {
const { local } = ctx.propsDestructuredBindings[key]
Expand Down
18 changes: 9 additions & 9 deletions packages/compiler-sfc/src/script/resolveType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
} from './utils'
import { type ScriptCompileContext, resolveParserPlugins } from './context'
import type { ImportBinding, SFCScriptCompileOptions } from '../compileScript'
import { capitalize, hasOwn } from '@vue/shared'
import { Empty, capitalize, hasOwn } from '@vue/shared'
import { parse as babelParse } from '@babel/parser'
import { parse } from '../parse'
import { createCache } from '../cache'
Expand Down Expand Up @@ -111,14 +111,14 @@ export class TypeScope {
public filename: string,
public source: string,
public offset: number = 0,
public imports: Record<string, Import> = Object.create(null),
public types: Record<string, ScopeTypeNode> = Object.create(null),
public declares: Record<string, ScopeTypeNode> = Object.create(null),
public imports: Record<string, Import> = new Empty(),
public types: Record<string, ScopeTypeNode> = new Empty(),
public declares: Record<string, ScopeTypeNode> = new Empty(),
) {}
isGenericScope = false
resolvedImportSources: Record<string, string> = Object.create(null)
exportedTypes: Record<string, ScopeTypeNode> = Object.create(null)
exportedDeclares: Record<string, ScopeTypeNode> = Object.create(null)
resolvedImportSources: Record<string, string> = new Empty()
exportedTypes: Record<string, ScopeTypeNode> = new Empty()
exportedDeclares: Record<string, ScopeTypeNode> = new Empty()
}

export interface MaybeWithScope {
Expand Down Expand Up @@ -231,7 +231,7 @@ function innerResolveTypeElements(
resolved.typeParameters &&
node.typeParameters
) {
typeParams = Object.create(null)
typeParams = new Empty()
resolved.typeParameters.params.forEach((p, i) => {
let param = typeParameters && typeParameters[p.name]
if (!param) param = node.typeParameters!.params[i]
Expand Down Expand Up @@ -1442,7 +1442,7 @@ function attachNamespace(
}

export function recordImports(body: Statement[]): Record<string, Import> {
const imports: TypeScope['imports'] = Object.create(null)
const imports: TypeScope['imports'] = new Empty()
for (const s of body) {
recordImport(s, imports)
}
Expand Down
3 changes: 2 additions & 1 deletion packages/compiler-sfc/src/style/pluginScoped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import {
} from 'postcss'
import selectorParser from 'postcss-selector-parser'
import { warn } from '../warn'
import { Empty } from '@vue/shared'

const animationNameRE = /^(-\w+-)?animation-name$/
const animationRE = /^(-\w+-)?animation$/

const scopedPlugin: PluginCreator<string> = (id = '') => {
const keyframes = Object.create(null)
const keyframes = new Empty()
const shortId = id.replace(/^data-v-/, '')

return {
Expand Down
4 changes: 2 additions & 2 deletions packages/runtime-core/src/apiCreateApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { warn } from './warning'
import { type VNode, cloneVNode, createVNode } from './vnode'
import type { RootHydrateFunction } from './hydration'
import { devtoolsInitApp, devtoolsUnmountApp } from './devtools'
import { NO, extend, isFunction, isObject } from '@vue/shared'
import { Empty, NO, extend, isFunction, isObject } from '@vue/shared'
import { version } from '.'
import { installAppCompatProperties } from './compat/global'
import type { NormalizedPropsOptions } from './componentProps'
Expand Down Expand Up @@ -234,7 +234,7 @@ export function createAppContext(): AppContext {
mixins: [],
components: {},
directives: {},
provides: Object.create(null),
provides: new Empty(),
optionsCache: new WeakMap(),
propsCache: new WeakMap(),
emitsCache: new WeakMap(),
Expand Down
6 changes: 3 additions & 3 deletions packages/runtime-core/src/compat/compatConfig.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { extend, hasOwn, isArray, isFunction } from '@vue/shared'
import { Empty, extend, hasOwn, isArray, isFunction } from '@vue/shared'
import {
type Component,
type ComponentInternalInstance,
Expand Down Expand Up @@ -425,8 +425,8 @@ export const deprecationData: Record<DeprecationTypes, DeprecationData> = {
},
}

const instanceWarned: Record<string, true> = Object.create(null)
const warnCount: Record<string, number> = Object.create(null)
const instanceWarned: Record<string, true> = new Empty()
const warnCount: Record<string, number> = new Empty()

// test only
let warningEnabled = true
Expand Down
5 changes: 3 additions & 2 deletions packages/runtime-core/src/compat/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
trigger,
} from '@vue/reactivity'
import {
Empty,
NOOP,
extend,
invokeArrayFns,
Expand Down Expand Up @@ -259,7 +260,7 @@ export function createCompatVue(
mergeBase[key] = isArray(superValue)
? superValue.slice()
: isObject(superValue)
? extend(Object.create(null), superValue)
? extend(new Empty(), superValue)
: superValue
}

Expand Down Expand Up @@ -640,7 +641,7 @@ function defineReactive(obj: any, key: string, val: any) {
if (i && obj === i.proxy) {
// target is a Vue instance - define on instance.ctx
defineReactiveSimple(i.ctx, key, val)
i.accessCache = Object.create(null)
i.accessCache = new Empty()
} else if (isReactive(obj)) {
obj[key] = val
} else {
Expand Down
6 changes: 3 additions & 3 deletions packages/runtime-core/src/compat/instanceEventEmitter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isArray } from '@vue/shared'
import { Empty, isArray } from '@vue/shared'
import type { ComponentInternalInstance } from '../component'
import { ErrorCodes, callWithAsyncErrorHandling } from '../errorHandling'
import { DeprecationTypes, assertCompatEnabled } from './compatConfig'
Expand All @@ -18,7 +18,7 @@ export function getRegistry(
): EventRegistry {
let events = eventRegistryMap.get(instance)
if (!events) {
eventRegistryMap.set(instance, (events = Object.create(null)))
eventRegistryMap.set(instance, (events = new Empty()))
}
return events!
}
Expand Down Expand Up @@ -69,7 +69,7 @@ export function off(
const vm = instance.proxy
// all
if (!event) {
eventRegistryMap.set(instance, Object.create(null))
eventRegistryMap.set(instance, new Empty())
return vm
}
// array of events
Expand Down
3 changes: 2 additions & 1 deletion packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import {
} from './componentEmits'
import {
EMPTY_OBJ,
Empty,
type IfAny,
NOOP,
ShapeFlags,
Expand Down Expand Up @@ -847,7 +848,7 @@ function setupStatefulComponent(
}
}
// 0. create render proxy property access cache
instance.accessCache = Object.create(null)
instance.accessCache = new Empty()
// 1. create public instance / render proxy
instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)
if (__DEV__) {
Expand Down
9 changes: 5 additions & 4 deletions packages/runtime-core/src/componentOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
currentInstance,
} from './component'
import {
Empty,
type LooseRequired,
NOOP,
type Prettify,
Expand Down Expand Up @@ -503,7 +504,7 @@ enum OptionTypes {
}

function createDuplicateChecker() {
const cache = Object.create(null)
const cache = new Empty()
return (type: OptionTypes, key: string) => {
if (cache[key]) {
warn(`${type} property "${key}" is already defined in ${cache[key]}.`)
Expand Down Expand Up @@ -1079,7 +1080,7 @@ function mergeAsArray<T = Function>(to: T[] | T | undefined, from: T | T[]) {
}

function mergeObjectOptions(to: Object | undefined, from: Object | undefined) {
return to ? extend(Object.create(null), to, from) : from
return to ? extend(new Empty(), to, from) : from
}

function mergeEmitsOrPropsOptions(
Expand All @@ -1099,7 +1100,7 @@ function mergeEmitsOrPropsOptions(
return [...new Set([...to, ...from])]
}
return extend(
Object.create(null),
new Empty(),
normalizePropsOrEmits(to),
normalizePropsOrEmits(from ?? {}),
)
Expand All @@ -1114,7 +1115,7 @@ function mergeWatchOptions(
) {
if (!to) return from
if (!from) return to
const merged = extend(Object.create(null), to)
const merged = extend(new Empty(), to)
for (const key in from) {
merged[key] = mergeAsArray(to[key], from[key])
}
Expand Down
3 changes: 2 additions & 1 deletion packages/runtime-core/src/componentProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import {
EMPTY_ARR,
EMPTY_OBJ,
Empty,
type IfAny,
PatchFlags,
camelize,
Expand Down Expand Up @@ -197,7 +198,7 @@ export function initProps(
const props: Data = {}
const attrs: Data = createInternalObject()

instance.propsDefaults = Object.create(null)
instance.propsDefaults = new Empty()

setFullProps(instance, rawProps, props, attrs)

Expand Down
3 changes: 2 additions & 1 deletion packages/runtime-core/src/componentPublicInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from './apiWatch'
import {
EMPTY_OBJ,
Empty,
type IfAny,
NOOP,
type Prettify,
Expand Down Expand Up @@ -365,7 +366,7 @@ const getPublicInstance = (
export const publicPropertiesMap: PublicPropertiesMap =
// Move PURE marker to new line to workaround compiler discarding it
// due to type annotation
/*@__PURE__*/ extend(Object.create(null), {
/*@__PURE__*/ extend(new Empty(), {
$: i => i,
$el: i => i.vnode.el,
$data: i => i.data,
Expand Down
4 changes: 2 additions & 2 deletions packages/runtime-core/src/components/BaseTransition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { warn } from '../warning'
import { isKeepAlive } from './KeepAlive'
import { toRaw } from '@vue/reactivity'
import { ErrorCodes, callWithAsyncErrorHandling } from '../errorHandling'
import { PatchFlags, ShapeFlags, isArray, isFunction } from '@vue/shared'
import { Empty, PatchFlags, ShapeFlags, isArray, isFunction } from '@vue/shared'
import { onBeforeUnmount, onMounted } from '../apiLifecycle'
import { isTeleport } from './Teleport'
import type { RendererElement } from '../renderer'
Expand Down Expand Up @@ -303,7 +303,7 @@ function getLeavingNodesForType(
const { leavingVNodes } = state
let leavingVNodesCache = leavingVNodes.get(vnode.type)!
if (!leavingVNodesCache) {
leavingVNodesCache = Object.create(null)
leavingVNodesCache = new Empty()
leavingVNodes.set(vnode.type, leavingVNodesCache)
}
return leavingVNodesCache
Expand Down
5 changes: 2 additions & 3 deletions packages/runtime-dom/src/apiCustomElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
warn,
} from '@vue/runtime-core'
import {
Empty,
camelize,
extend,
hasOwn,
Expand Down Expand Up @@ -373,9 +374,7 @@ export class VueElement
if (key in this._props) {
this._props[key] = toNumber(this._props[key])
}
;(numberProps || (numberProps = Object.create(null)))[
camelize(key)
] = true
;(numberProps || (numberProps = new Empty()))[camelize(key)] = true
}
}
}
Expand Down
Loading
Loading