From 2ffe3d5b3e953b63d4743b1e2bc242d50916b545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=B6=E8=BF=9C=E6=96=B9?= Date: Tue, 22 Aug 2023 16:57:15 +0800 Subject: [PATCH] refactor: use symbol for private properties (#8681) --- .../src/components/BaseTransition.ts | 31 ++++++++++--------- .../runtime-dom/__tests__/patchClass.spec.ts | 6 ++-- .../runtime-dom/src/components/Transition.ts | 12 ++++--- .../src/components/TransitionGroup.ts | 23 ++++++++------ packages/runtime-dom/src/directives/vModel.ts | 28 +++++++++-------- packages/runtime-dom/src/directives/vShow.ts | 8 +++-- packages/runtime-dom/src/modules/class.ts | 4 +-- packages/runtime-dom/src/modules/events.ts | 6 ++-- packages/runtime-dom/src/modules/style.ts | 3 +- packages/vue/__tests__/svgNamespace.spec.ts | 3 +- 10 files changed, 70 insertions(+), 54 deletions(-) diff --git a/packages/runtime-core/src/components/BaseTransition.ts b/packages/runtime-core/src/components/BaseTransition.ts index a4e862335eb..9cb80b94ef0 100644 --- a/packages/runtime-core/src/components/BaseTransition.ts +++ b/packages/runtime-core/src/components/BaseTransition.ts @@ -22,6 +22,9 @@ import { RendererElement } from '../renderer' type Hook void> = T | T[] +const leaveCbKey = Symbol('_leaveCb') +const enterCbKey = Symbol('_enterCb') + export interface BaseTransitionProps { mode?: 'in-out' | 'out-in' | 'default' appear?: boolean @@ -89,8 +92,8 @@ export interface TransitionElement { // in persisted mode (e.g. v-show), the same element is toggled, so the // pending enter/leave callbacks may need to be cancelled if the state is toggled // before it finishes. - _enterCb?: PendingCallback - _leaveCb?: PendingCallback + [enterCbKey]?: PendingCallback + [leaveCbKey]?: PendingCallback } export function useTransitionState(): TransitionState { @@ -259,9 +262,9 @@ const BaseTransitionImpl: ComponentOptions = { ) leavingVNodesCache[String(oldInnerChild.key)] = oldInnerChild // early removal callback - el._leaveCb = () => { + el[leaveCbKey] = () => { earlyRemove() - el._leaveCb = undefined + el[leaveCbKey] = undefined delete enterHooks.delayedLeave } enterHooks.delayedLeave = delayedLeave @@ -366,18 +369,18 @@ export function resolveTransitionHooks( } } // for same element (v-show) - if (el._leaveCb) { - el._leaveCb(true /* cancelled */) + if (el[leaveCbKey]) { + el[leaveCbKey](true /* cancelled */) } // for toggled element with same key (v-if) const leavingVNode = leavingVNodesCache[key] if ( leavingVNode && isSameVNodeType(vnode, leavingVNode) && - leavingVNode.el!._leaveCb + (leavingVNode.el as TransitionElement)[leaveCbKey] ) { // force early removal (not cancelled) - leavingVNode.el!._leaveCb() + ;(leavingVNode.el as TransitionElement)[leaveCbKey]!() } callHook(hook, [el]) }, @@ -396,7 +399,7 @@ export function resolveTransitionHooks( } } let called = false - const done = (el._enterCb = (cancelled?) => { + const done = (el[enterCbKey] = (cancelled?) => { if (called) return called = true if (cancelled) { @@ -407,7 +410,7 @@ export function resolveTransitionHooks( if (hooks.delayedLeave) { hooks.delayedLeave() } - el._enterCb = undefined + el[enterCbKey] = undefined }) if (hook) { callAsyncHook(hook, [el, done]) @@ -418,15 +421,15 @@ export function resolveTransitionHooks( leave(el, remove) { const key = String(vnode.key) - if (el._enterCb) { - el._enterCb(true /* cancelled */) + if (el[enterCbKey]) { + el[enterCbKey](true /* cancelled */) } if (state.isUnmounting) { return remove() } callHook(onBeforeLeave, [el]) let called = false - const done = (el._leaveCb = (cancelled?) => { + const done = (el[leaveCbKey] = (cancelled?) => { if (called) return called = true remove() @@ -435,7 +438,7 @@ export function resolveTransitionHooks( } else { callHook(onAfterLeave, [el]) } - el._leaveCb = undefined + el[leaveCbKey] = undefined if (leavingVNodesCache[key] === vnode) { delete leavingVNodesCache[key] } diff --git a/packages/runtime-dom/__tests__/patchClass.spec.ts b/packages/runtime-dom/__tests__/patchClass.spec.ts index a784c7d543f..c8da741677a 100644 --- a/packages/runtime-dom/__tests__/patchClass.spec.ts +++ b/packages/runtime-dom/__tests__/patchClass.spec.ts @@ -1,5 +1,5 @@ import { patchProp } from '../src/patchProp' -import { ElementWithTransition } from '../src/components/Transition' +import { ElementWithTransition, vtcKey } from '../src/components/Transition' import { svgNS } from '../src/nodeOps' describe('runtime-dom: class patching', () => { @@ -13,12 +13,12 @@ describe('runtime-dom: class patching', () => { test('transition class', () => { const el = document.createElement('div') as ElementWithTransition - el._vtc = new Set(['bar', 'baz']) + el[vtcKey] = new Set(['bar', 'baz']) patchProp(el, 'class', null, 'foo') expect(el.className).toBe('foo bar baz') patchProp(el, 'class', null, null) expect(el.className).toBe('bar baz') - delete el._vtc + delete el[vtcKey] patchProp(el, 'class', null, 'foo') expect(el.className).toBe('foo') }) diff --git a/packages/runtime-dom/src/components/Transition.ts b/packages/runtime-dom/src/components/Transition.ts index eebfdccca35..b0675213298 100644 --- a/packages/runtime-dom/src/components/Transition.ts +++ b/packages/runtime-dom/src/components/Transition.ts @@ -32,12 +32,14 @@ export interface TransitionProps extends BaseTransitionProps { leaveToClass?: string } +export const vtcKey = Symbol('_vtc') + export interface ElementWithTransition extends HTMLElement { // _vtc = Vue Transition Classes. // Store the temporarily-added transition classes on the element // so that we can avoid overwriting them if the element's class is patched // during the transition. - _vtc?: Set + [vtcKey]?: Set } // DOM Transition is a higher-order-component based on the platform-agnostic @@ -295,18 +297,18 @@ function NumberOf(val: unknown): number { export function addTransitionClass(el: Element, cls: string) { cls.split(/\s+/).forEach(c => c && el.classList.add(c)) ;( - (el as ElementWithTransition)._vtc || - ((el as ElementWithTransition)._vtc = new Set()) + (el as ElementWithTransition)[vtcKey] || + ((el as ElementWithTransition)[vtcKey] = new Set()) ).add(cls) } export function removeTransitionClass(el: Element, cls: string) { cls.split(/\s+/).forEach(c => c && el.classList.remove(c)) - const { _vtc } = el as ElementWithTransition + const _vtc = (el as ElementWithTransition)[vtcKey] if (_vtc) { _vtc.delete(cls) if (!_vtc!.size) { - ;(el as ElementWithTransition)._vtc = undefined + ;(el as ElementWithTransition)[vtcKey] = undefined } } } diff --git a/packages/runtime-dom/src/components/TransitionGroup.ts b/packages/runtime-dom/src/components/TransitionGroup.ts index 5c78be26d72..fc5d260b91e 100644 --- a/packages/runtime-dom/src/components/TransitionGroup.ts +++ b/packages/runtime-dom/src/components/TransitionGroup.ts @@ -6,7 +6,8 @@ import { getTransitionInfo, resolveTransitionProps, TransitionPropsValidators, - forceReflow + forceReflow, + vtcKey } from './Transition' import { Fragment, @@ -29,7 +30,8 @@ import { extend } from '@vue/shared' const positionMap = new WeakMap() const newPositionMap = new WeakMap() - +const moveCbKey = Symbol('_moveCb') +const enterCbKey = Symbol('_enterCb') export type TransitionGroupProps = Omit & { tag?: string moveClass?: string @@ -80,13 +82,13 @@ const TransitionGroupImpl: ComponentOptions = { const style = el.style addTransitionClass(el, moveClass) style.transform = style.webkitTransform = style.transitionDuration = '' - const cb = ((el as any)._moveCb = (e: TransitionEvent) => { + const cb = ((el as any)[moveCbKey] = (e: TransitionEvent) => { if (e && e.target !== el) { return } if (!e || /transform$/.test(e.propertyName)) { el.removeEventListener('transitionend', cb) - ;(el as any)._moveCb = null + ;(el as any)[moveCbKey] = null removeTransitionClass(el, moveClass) } }) @@ -162,11 +164,11 @@ export const TransitionGroup = TransitionGroupImpl as unknown as { function callPendingCbs(c: VNode) { const el = c.el as any - if (el._moveCb) { - el._moveCb() + if (el[moveCbKey]) { + el[moveCbKey]() } - if (el._enterCb) { - el._enterCb() + if (el[enterCbKey]) { + el[enterCbKey]() } } @@ -198,8 +200,9 @@ function hasCSSTransform( // all other transition classes applied to ensure only the move class // is applied. const clone = el.cloneNode() as HTMLElement - if (el._vtc) { - el._vtc.forEach(cls => { + const _vtc = el[vtcKey] + if (_vtc) { + _vtc.forEach(cls => { cls.split(/\s+/).forEach(c => c && clone.classList.remove(c)) }) } diff --git a/packages/runtime-dom/src/directives/vModel.ts b/packages/runtime-dom/src/directives/vModel.ts index 2cf5f4cfc16..89cd5f9d49f 100644 --- a/packages/runtime-dom/src/directives/vModel.ts +++ b/packages/runtime-dom/src/directives/vModel.ts @@ -36,7 +36,9 @@ function onCompositionEnd(e: Event) { } } -type ModelDirective = ObjectDirective +const assignKey = Symbol('_assign') + +type ModelDirective = ObjectDirective // We are exporting the v-model runtime directly as vnode hooks so that it can // be tree-shaken in case v-model is never used. @@ -44,7 +46,7 @@ export const vModelText: ModelDirective< HTMLInputElement | HTMLTextAreaElement > = { created(el, { modifiers: { lazy, trim, number } }, vnode) { - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) const castToNumber = number || (vnode.props && vnode.props.type === 'number') addEventListener(el, lazy ? 'change' : 'input', e => { @@ -56,7 +58,7 @@ export const vModelText: ModelDirective< if (castToNumber) { domValue = looseToNumber(domValue) } - el._assign(domValue) + el[assignKey](domValue) }) if (trim) { addEventListener(el, 'change', () => { @@ -78,7 +80,7 @@ export const vModelText: ModelDirective< el.value = value == null ? '' : value }, beforeUpdate(el, { value, modifiers: { lazy, trim, number } }, vnode) { - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) // avoid clearing unresolved text. #2302 if ((el as any).composing) return if (document.activeElement === el && el.type !== 'range') { @@ -106,12 +108,12 @@ export const vModelCheckbox: ModelDirective = { // #4096 array checkboxes need to be deep traversed deep: true, created(el, _, vnode) { - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) addEventListener(el, 'change', () => { const modelValue = (el as any)._modelValue const elementValue = getValue(el) const checked = el.checked - const assign = el._assign + const assign = el[assignKey] if (isArray(modelValue)) { const index = looseIndexOf(modelValue, elementValue) const found = index !== -1 @@ -138,7 +140,7 @@ export const vModelCheckbox: ModelDirective = { // set initial checked on mount to wait for true-value/false-value mounted: setChecked, beforeUpdate(el, binding, vnode) { - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) setChecked(el, binding, vnode) } } @@ -163,13 +165,13 @@ function setChecked( export const vModelRadio: ModelDirective = { created(el, { value }, vnode) { el.checked = looseEqual(value, vnode.props!.value) - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) addEventListener(el, 'change', () => { - el._assign(getValue(el)) + el[assignKey](getValue(el)) }) }, beforeUpdate(el, { value, oldValue }, vnode) { - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) if (value !== oldValue) { el.checked = looseEqual(value, vnode.props!.value) } @@ -187,7 +189,7 @@ export const vModelSelect: ModelDirective = { .map((o: HTMLOptionElement) => number ? looseToNumber(getValue(o)) : getValue(o) ) - el._assign( + el[assignKey]( el.multiple ? isSetModel ? new Set(selectedVal) @@ -195,7 +197,7 @@ export const vModelSelect: ModelDirective = { : selectedVal[0] ) }) - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) }, // set value in mounted & updated because