8000 refactor: use symbol for private properties by Alfred-Skyblue · Pull Request #8681 · vuejs/core · GitHub
[go: up one dir, main page]

Skip to content

refactor: use symbol for private properties #8681

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

Merged
merged 4 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
chore: extend HTMLElement with Symbol as key
  • Loading branch information
Alfred-Skyblue authored and sxzz committed Aug 21, 2023
commit d53bf8a9cb56389a181c03d002ecf7caf6707bb2
31 changes: 17 additions & 14 deletions packages/runtime-core/src/components/BaseTransition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import { RendererElement } from '../renderer'

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

const leaveCbKey = Symbol('_leaveCb')
const enterCbKey = Symbol('_enterCb')

export interface BaseTransitionProps<HostElement = RendererElement> {
mode?: 'in-out' | 'out-in' | 'default'
appear?: boolean
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 = 10000 delayedLeave
Expand Down Expand Up @@ -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])
},
Expand All @@ -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) {
Expand All @@ -407,7 +410,7 @@ export function resolveTransitionHooks(
if (hooks.delayedLeave) {
hooks.delayedLeave()
}
el._enterCb = undefined
el[enterCbKey] = undefined
})
if (hook) {
callAsyncHook(hook, [el, done])
Expand All @@ -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()
Expand All @@ -435,7 +438,7 @@ export function resolveTransitionHooks(
} else {
callHook(onAfterLeave, [el])
}
el._leaveCb = undefined
el[leaveCbKey] = undefined
if (leavingVNodesCache[key] === vnode) {
delete leavingVNodesCache[key]
}
Expand Down
20 changes: 11 additions & 9 deletions packages/runtime-dom/src/components/TransitionGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ import { extend } from '@vue/shared'

const positionMap = new WeakMap<VNode, DOMRect>()
const newPositionMap = new WeakMap<VNode, DOMRect>()

const moveCbKey = Symbol('_moveCb')
const enterCbKey = Symbol('_enterCb')
export type TransitionGroupProps = Omit<TransitionProps, 'mode'> & {
tag?: string
moveClass?: string
Expand Down Expand Up @@ -80,13 +81,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)
}
})
Expand Down Expand Up @@ -162,11 +163,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]()
}
}

Expand Down Expand Up @@ -198,8 +199,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._vtc
if (_vtc) {
_vtc.forEach(cls => {
cls.split(/\s+/).forEach(c => c && clone.classList.remove(c))
})
}
Expand Down
28 changes: 15 additions & 13 deletions packages/runtime-dom/src/directives/vModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,17 @@ function onCompositionEnd(e: Event) {
}
}

type ModelDirective<T> = ObjectDirective<T & { _assign: AssignerFn }>
const assignKey = Symbol('_assign')

type ModelDirective<T> = ObjectDirective<T & { [assignKey]: AssignerFn }>

// 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.
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 => {
Expand All @@ -56,7 +58,7 @@ export const vModelText: ModelDirective<
if (castToNumber) {
domValue = looseToNumber(domValue)
}
el._assign(domValue)
el[assignKey](domValue)
})
if (trim) {
addEventListener(el, 'change', () => {
Expand All @@ -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') {
Expand Down Expand Up @@ -106,12 +108,12 @@ export const vModelCheckbox: ModelDirective<HTMLInputElement> = {
// #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)
F438 const checked = el.checked
const assign = el._assign
const assign = el[assignKey]
if (isArray(modelValue)) {
const index = looseIndexOf(modelValue, elementValue)
const found = index !== -1
Expand All @@ -138,7 +140,7 @@ export const vModelCheckbox: ModelDirective<HTMLInputElement> = {
// 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)
}
}
Expand All @@ -163,13 +165,13 @@ function setChecked(
export const vModelRadio: ModelDirective<HTMLInputElement> = {
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)
}
Expand All @@ -187,23 +189,23 @@ export const vModelSelect: ModelDirective<HTMLSelectElement> = {
.map((o: HTMLOptionElement) =>
number ? looseToNumber(getValue(o)) : getValue(o)
)
el._assign(
el[assignKey](
el.multiple
? isSetModel
? new Set(selectedVal)
: selectedVal
: selectedVal[0]
)
})
el._assign = getModelAssigner(vnode)
el[assignKey] = getModelAssigner(vnode)
},
// set value in mounted & updated because <select> relies on its children
// <option>s.
mounted(el, { value }) {
setSelected(el, value)
},
beforeUpdate(el, _binding, vnode) {
el._assign = getModelAssigner(vnode)
el[assignKey] = getModelAssigner(vnode)
},
updated(el, { value }) {
setSelected(el, value)
Expand Down
8 changes: 5 additions & 3 deletions packages/runtime-dom/src/directives/vShow.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { ObjectDirective } from '@vue/runtime-core'

export const vShowOldKey = Symbol('_vod')

interface VShowElement extends HTMLElement {
// _vod = vue original display
_vod: string
[vShowOldKey]: string
}

export const vShow: ObjectDirective<VShowElement> = {
beforeMount(el, { value }, { transition }) {
el._vod = el.style.display === 'none' ? '' : el.style.display
el[vShowOldKey] = el.style.display === 'none' ? '' : el.style.display
if (transition && value) {
transition.beforeEnter(el)
} else {
Expand Down Expand Up @@ -41,7 +43,7 @@ export const vShow: ObjectDirective<VShowElement> = {
}

function setDisplay(el: VShowElement, value: unknown): void {
el.style.display = value ? el._vod : 'none'
el.style.display = value ? el[vShowOldKey] : 'none'
}

// SSR vnode transforms, only used when user includes client-oriented render
Expand Down
6 changes: 4 additions & 2 deletions packages/runtime-dom/src/modules/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,17 @@ export function removeEventListener(
el.removeEventListener(event, handler, options)
}

const veiKey = Symbol('_vei')

export function patchEvent(
el: Element & { _vei?: Record<string, Invoker | undefined> },
el: Element & { [veiKey]?: Record<string, Invoker | undefined> },
rawName: string,
prevValue: EventValue | null,
nextValue: EventValue | null,
instance: ComponentInternalInstance | null = null
) {
// vei = vue event invokers
const invokers = el._vei || (el._vei = {})
const invokers = el[veiKey] || (el[veiKey] = {})
const existingInvoker = invokers[rawName]
if (nextValue && existingInvoker) {
// patch
Expand Down
3 changes: 2 additions & 1 deletion packages/runtime-dom/src/modules/style.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { isString, hyphenate, capitalize, isArray } from '@vue/shared'
import { camelize, warn } from '@vue/runtime-core'
import { vShowOldKey } from '../directives/vShow'

type Style = string | Record<string, string | string[]> | null

Expand Down Expand Up @@ -29,7 +30,7 @@ export function patchStyle(el: Element, prev: Style, next: Style) {
// indicates that the `display` of the element is controlled by `v-show`,
// so we always keep the current `display` value regardless of the `style`
// value, thus handing over control to `v-show`.
if ('_vod' in el) {
if (vShowOldKey in el) {
style.display = currentDisplay
}
}
Expand Down
0