Skip to content

Commit 2ffe3d5

Browse files
refactor: use symbol for private properties (#8681)
1 parent 02c6924 commit 2ffe3d5

File tree

10 files changed

+70
-54
lines changed

10 files changed

+70
-54
lines changed

packages/runtime-core/src/components/BaseTransition.ts

+17-14
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import { RendererElement } from '../renderer'
2222

2323
type Hook<T = () => void> = T | T[]
2424

25+
const leaveCbKey = Symbol('_leaveCb')
26+
const enterCbKey = Symbol('_enterCb')
27+
2528
export interface BaseTransitionProps<HostElement = RendererElement> {
2629
mode?: 'in-out' | 'out-in' | 'default'
2730
appear?: boolean
@@ -89,8 +92,8 @@ export interface TransitionElement {
8992
// in persisted mode (e.g. v-show), the same element is toggled, so the
9093
// pending enter/leave callbacks may need to be cancelled if the state is toggled
9194
// before it finishes.
92-
_enterCb?: PendingCallback
93-
_leaveCb?: PendingCallback
95+
[enterCbKey]?: PendingCallback
96+
[leaveCbKey]?: PendingCallback
9497
}
9598

9699
export function useTransitionState(): TransitionState {
@@ -259,9 +262,9 @@ const BaseTransitionImpl: ComponentOptions = {
259262
)
260263
leavingVNodesCache[String(oldInnerChild.key)] = oldInnerChild
261264
// early removal callback
262-
el._leaveCb = () => {
265+
el[leaveCbKey] = () => {
263266
earlyRemove()
264-
el._leaveCb = undefined
267+
el[leaveCbKey] = undefined
265268
delete enterHooks.delayedLeave
266269
}
267270
enterHooks.delayedLeave = delayedLeave
@@ -366,18 +369,18 @@ export function resolveTransitionHooks(
366369
}
367370
}
368371
// for same element (v-show)
369-
if (el._leaveCb) {
370-
el._leaveCb(true /* cancelled */)
372+
if (el[leaveCbKey]) {
373+
el[leaveCbKey](true /* cancelled */)
371374
}
372375
// for toggled element with same key (v-if)
373376
const leavingVNode = leavingVNodesCache[key]
374377
if (
375378
leavingVNode &&
376379
isSameVNodeType(vnode, leavingVNode) &&
377-
leavingVNode.el!._leaveCb
380+
(leavingVNode.el as TransitionElement)[leaveCbKey]
378381
) {
379382
// force early removal (not cancelled)
380-
leavingVNode.el!._leaveCb()
383+
;(leavingVNode.el as TransitionElement)[leaveCbKey]!()
381384
}
382385
callHook(hook, [el])
383386
},
@@ -396,7 +399,7 @@ export function resolveTransitionHooks(
396399
}
397400
}
398401
let called = false
399-
const done = (el._enterCb = (cancelled?) => {
402+
const done = (el[enterCbKey] = (cancelled?) => {
400403
if (called) return
401404
called = true
402405
if (cancelled) {
@@ -407,7 +410,7 @@ export function resolveTransitionHooks(
407410
if (hooks.delayedLeave) {
408411
hooks.delayedLeave()
409412
}
410-
el._enterCb = undefined
413+
el[enterCbKey] = undefined
411414
})
412415
if (hook) {
413416
callAsyncHook(hook, [el, done])
@@ -418,15 +421,15 @@ export function resolveTransitionHooks(
418421

419422
leave(el, remove) {
420423
const key = String(vnode.key)
421-
if (el._enterCb) {
422-
el._enterCb(true /* cancelled */)
424+
if (el[enterCbKey]) {
425+
el[enterCbKey](true /* cancelled */)
423426
}
424427
if (state.isUnmounting) {
425428
return remove()
426429
}
427430
callHook(onBeforeLeave, [el])
428431
let called = false
429-
const done = (el._leaveCb = (cancelled?) => {
432+
const done = (el[leaveCbKey] = (cancelled?) => {
430433
if (called) return
431434
called = true
432435
remove()
@@ -435,7 +438,7 @@ export function resolveTransitionHooks(
435438
} else {
436439
callHook(onAfterLeave, [el])
437440
}
438-
el._leaveCb = undefined
441+
el[leaveCbKey] = undefined
439442
if (leavingVNodesCache[key] === vnode) {
440443
delete leavingVNodesCache[key]
441444
}

packages/runtime-dom/__tests__/patchClass.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { patchProp } from '../src/patchProp'
2-
import { ElementWithTransition } from '../src/components/Transition'
2+
import { ElementWithTransition, vtcKey } from '../src/components/Transition'
33
import { svgNS } from '../src/nodeOps'
44

55
describe('runtime-dom: class patching', () => {
@@ -13,12 +13,12 @@ describe('runtime-dom: class patching', () => {
1313

1414
test('transition class', () => {
1515
const el = document.createElement('div') as ElementWithTransition
16-
el._vtc = new Set(['bar', 'baz'])
16+
el[vtcKey] = new Set(['bar', 'baz'])
1717
patchProp(el, 'class', null, 'foo')
1818
expect(el.className).toBe('foo bar baz')
1919
patchProp(el, 'class', null, null)
2020
expect(el.className).toBe('bar baz')
21-
delete el._vtc
21+
delete el[vtcKey]
2222
patchProp(el, 'class', null, 'foo')
2323
expect(el.className).toBe('foo')
2424
})

packages/runtime-dom/src/components/Transition.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@ export interface TransitionProps extends BaseTransitionProps<Element> {
3232
leaveToClass?: string
3333
}
3434

35+
export const vtcKey = Symbol('_vtc')
36+
3537
export interface ElementWithTransition extends HTMLElement {
3638
// _vtc = Vue Transition Classes.
3739
// Store the temporarily-added transition classes on the element
3840
// so that we can avoid overwriting them if the element's class is patched
3941
// during the transition.
40-
_vtc?: Set<string>
42+
[vtcKey]?: Set<string>
4143
}
4244

4345
// DOM Transition is a higher-order-component based on the platform-agnostic
@@ -295,18 +297,18 @@ function NumberOf(val: unknown): number {
295297
export function addTransitionClass(el: Element, cls: string) {
296298
cls.split(/\s+/).forEach(c => c && el.classList.add(c))
297299
;(
298-
(el as ElementWithTransition)._vtc ||
299-
((el as ElementWithTransition)._vtc = new Set())
300+
(el as ElementWithTransition)[vtcKey] ||
301+
((el as ElementWithTransition)[vtcKey] = new Set())
300302
).add(cls)
301303
}
302304

303305
export function removeTransitionClass(el: Element, cls: string) {
304306
cls.split(/\s+/).forEach(c => c && el.classList.remove(c))
305-
const { _vtc } = el as ElementWithTransition
307+
const _vtc = (el as ElementWithTransition)[vtcKey]
306308
if (_vtc) {
307309
_vtc.delete(cls)
308310
if (!_vtc!.size) {
309-
;(el as ElementWithTransition)._vtc = undefined
311+
;(el as ElementWithTransition)[vtcKey] = undefined
310312
}
311313
}
312314
}

packages/runtime-dom/src/components/TransitionGroup.ts

+13-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
getTransitionInfo,
77
resolveTransitionProps,
88
TransitionPropsValidators,
9-
forceReflow
9+
forceReflow,
10+
vtcKey
1011
} from './Transition'
1112
import {
1213
Fragment,
@@ -29,7 +30,8 @@ import { extend } from '@vue/shared'
2930

3031
const positionMap = new WeakMap<VNode, DOMRect>()
3132
const newPositionMap = new WeakMap<VNode, DOMRect>()
32-
33+
const moveCbKey = Symbol('_moveCb')
34+
const enterCbKey = Symbol('_enterCb')
3335
export type TransitionGroupProps = Omit<TransitionProps, 'mode'> & {
3436
tag?: string
3537
moveClass?: string
@@ -80,13 +82,13 @@ const TransitionGroupImpl: ComponentOptions = {
8082
const style = el.style
8183
addTransitionClass(el, moveClass)
8284
style.transform = style.webkitTransform = style.transitionDuration = ''
83-
const cb = ((el as any)._moveCb = (e: TransitionEvent) => {
85+
const cb = ((el as any)[moveCbKey] = (e: TransitionEvent) => {
8486
if (e && e.target !== el) {
8587
return
8688
}
8789
if (!e || /transform$/.test(e.propertyName)) {
8890
el.removeEventListener('transitionend', cb)
89-
;(el as any)._moveCb = null
91+
;(el as any)[moveCbKey] = null
9092
removeTransitionClass(el, moveClass)
9193
}
9294
})
@@ -162,11 +164,11 @@ export const TransitionGroup = TransitionGroupImpl as unknown as {
162164

163165
function callPendingCbs(c: VNode) {
164166
const el = c.el as any
165-
if (el._moveCb) {
166-
el._moveCb()
167+
if (el[moveCbKey]) {
168+
el[moveCbKey]()
167169
}
168-
if (el._enterCb) {
169-
el._enterCb()
170+
if (el[enterCbKey]) {
171+
el[enterCbKey]()
170172
}
171173
}
172174

@@ -198,8 +200,9 @@ function hasCSSTransform(
198200
// all other transition classes applied to ensure only the move class
199201
// is applied.
200202
const clone = el.cloneNode() as HTMLElement
201-
if (el._vtc) {
202-
el._vtc.forEach(cls => {
203+
const _vtc = el[vtcKey]
204+
if (_vtc) {
205+
_vtc.forEach(cls => {
203206
cls.split(/\s+/).forEach(c => c && clone.classList.remove(c))
204207
})
205208
}

packages/runtime-dom/src/directives/vModel.ts

+15-13
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,17 @@ function onCompositionEnd(e: Event) {
3636
}
3737
}
3838

39-
type ModelDirective<T> = ObjectDirective<T & { _assign: AssignerFn }>
39+
const assignKey = Symbol('_assign')
40+
41+
type ModelDirective<T> = ObjectDirective<T & { [assignKey]: AssignerFn }>
4042

4143
// We are exporting the v-model runtime directly as vnode hooks so that it can
4244
// be tree-shaken in case v-model is never used.
4345
export const vModelText: ModelDirective<
4446
HTMLInputElement | HTMLTextAreaElement
4547
> = {
4648
created(el, { modifiers: { lazy, trim, number } }, vnode) {
47-
el._assign = getModelAssigner(vnode)
49+
el[assignKey] = getModelAssigner(vnode)
4850
const castToNumber =
4951
number || (vnode.props && vnode.props.type === 'number')
5052
addEventListener(el, lazy ? 'change' : 'input', e => {
@@ -56,7 +58,7 @@ export const vModelText: ModelDirective<
5658
if (castToNumber) {
5759
domValue = looseToNumber(domValue)
5860
}
59-
el._assign(domValue)
61+
el[assignKey](domValue)
6062
})
6163
if (trim) {
6264
addEventListener(el, 'change', () => {
@@ -78,7 +80,7 @@ export const vModelText: ModelDirective<
7880
el.value = value == null ? '' : value
7981
},
8082
beforeUpdate(el, { value, modifiers: { lazy, trim, number } }, vnode) {
81-
el._assign = getModelAssigner(vnode)
83+
el[assignKey] = getModelAssigner(vnode)
8284
// avoid clearing unresolved text. #2302
8385
if ((el as any).composing) return
8486
if (document.activeElement === el && el.type !== 'range') {
@@ -106,12 +108,12 @@ export const vModelCheckbox: ModelDirective<HTMLInputElement> = {
106108
// #4096 array checkboxes need to be deep traversed
107109
deep: true,
108110
created(el, _, vnode) {
109-
el._assign = getModelAssigner(vnode)
111+
el[assignKey] = getModelAssigner(vnode)
110112
addEventListener(el, 'change', () => {
111113
const modelValue = (el as any)._modelValue
112114
const elementValue = getValue(el)
113115
const checked = el.checked
114-
const assign = el._assign
116+
const assign = el[assignKey]
115117
if (isArray(modelValue)) {
116118
const index = looseIndexOf(modelValue, elementValue)
117119
const found = index !== -1
@@ -138,7 +140,7 @@ export const vModelCheckbox: ModelDirective<HTMLInputElement> = {
138140
// set initial checked on mount to wait for true-value/false-value
139141
mounted: setChecked,
140142
beforeUpdate(el, binding, vnode) {
141-
el._assign = getModelAssigner(vnode)
143+
el[assignKey] = getModelAssigner(vnode)
142144
setChecked(el, binding, vnode)
143145
}
144146
}
@@ -163,13 +165,13 @@ function setChecked(
163165
export const vModelRadio: ModelDirective<HTMLInputElement> = {
164166
created(el, { value }, vnode) {
165167
el.checked = looseEqual(value, vnode.props!.value)
166-
el._assign = getModelAssigner(vnode)
168+
el[assignKey] = getModelAssigner(vnode)
167169
addEventListener(el, 'change', () => {
168-
el._assign(getValue(el))
170+
el[assignKey](getValue(el))
169171
})
170172
},
171173
beforeUpdate(el, { value, oldValue }, vnode) {
172-
el._assign = getModelAssigner(vnode)
174+
el[assignKey] = getModelAssigner(vnode)
173175
if (value !== oldValue) {
174176
el.checked = looseEqual(value, vnode.props!.value)
175177
}
@@ -187,23 +189,23 @@ export const vModelSelect: ModelDirective<HTMLSelectElement> = {
187189
.map((o: HTMLOptionElement) =>
188190
number ? looseToNumber(getValue(o)) : getValue(o)
189191
)
190-
el._assign(
192+
el[assignKey](
191193
el.multiple
192194
? isSetModel
193195
? new Set(selectedVal)
194196
: selectedVal
195197
: selectedVal[0]
196198
)
197199
})
198-
el._assign = getModelAssigner(vnode)
200+
el[assignKey] = getModelAssigner(vnode)
199201
},
200202
// set value in mounted & updated because <select> relies on its children
201203
// <option>s.
202204
mounted(el, { value }) {
203205
setSelected(el, value)
204206
},
205207
beforeUpdate(el, _binding, vnode) {
206-
el._assign = getModelAssigner(vnode)
208+
el[assignKey] = getModelAssigner(vnode)
207209
},
208210
updated(el, { value }) {
209211
setSelected(el, value)

packages/runtime-dom/src/directives/vShow.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { ObjectDirective } from '@vue/runtime-core'
22

3+
export const vShowOldKey = Symbol('_vod')
4+
35
interface VShowElement extends HTMLElement {
46
// _vod = vue original display
5-
_vod: string
7+
[vShowOldKey]: string
68
}
79

810
export const vShow: ObjectDirective<VShowElement> = {
911
beforeMount(el, { value }, { transition }) {
10-
el._vod = el.style.display === 'none' ? '' : el.style.display
12+
el[vShowOldKey] = el.style.display === 'none' ? '' : el.style.display
1113
if (transition && value) {
1214
transition.beforeEnter(el)
1315
} else {
@@ -41,7 +43,7 @@ export const vShow: ObjectDirective<VShowElement> = {
4143
}
4244

4345
function setDisplay(el: VShowElement, value: unknown): void {
44-
el.style.display = value ? el._vod : 'none'
46+
el.style.display = value ? el[vShowOldKey] : 'none'
4547
}
4648

4749
// SSR vnode transforms, only used when user includes client-oriented render

packages/runtime-dom/src/modules/class.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { ElementWithTransition } from '../components/Transition'
1+
import { ElementWithTransition, vtcKey } from '../components/Transition'
22

33
// compiler should normalize class + :class bindings on the same element
44
// into a single binding ['staticClass', dynamic]
55
export function patchClass(el: Element, value: string | null, isSVG: boolean) {
66
// directly setting className should be faster than setAttribute in theory
77
// if this is an element during a transition, take the temporary transition
88
// classes into account.
9-
const transitionClasses = (el as ElementWithTransition)._vtc
9+
const transitionClasses = (el as ElementWithTransition)[vtcKey]
1010
if (transitionClasses) {
1111
value = (
1212
value ? [value, ...transitionClasses] : [...transitionClasses]

packages/runtime-dom/src/modules/events.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,17 @@ export function removeEventListener(
3030
el.removeEventListener(event, handler, options)
3131
}
3232

33+
const veiKey = Symbol('_vei')
34+
3335
export function patchEvent(
34-
el: Element & { _vei?: Record<string, Invoker | undefined> },
36+
el: Element & { [veiKey]?: Record<string, Invoker | undefined> },
3537
rawName: string,
3638
prevValue: EventValue | null,
3739
nextValue: EventValue | null,
3840
instance: ComponentInternalInstance | null = null
3941
) {
4042
// vei = vue event invokers
41-
const invokers = el._vei || (el._vei = {})
43+
const invokers = el[veiKey] || (el[veiKey] = {})
4244
const existingInvoker = invokers[rawName]
4345
if (nextValue && existingInvoker) {
4446
// patch

0 commit comments

Comments
 (0)