refactor(runtime-vapor): record event metadata as array

This commit is contained in:
三咲智子 Kevin Deng 2024-03-01 18:15:00 +08:00
parent 7f861441d7
commit 5a0bc110d9
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
6 changed files with 47 additions and 47 deletions

View File

@ -1,12 +1,12 @@
import { insert, normalizeBlock, prepend, remove } from '../src/dom/element' import { insert, normalizeBlock, prepend, remove } from '../../src/dom/element'
import { fragmentKey } from '../src/render' import { fragmentKey } from '../../src/render'
const node1 = document.createTextNode('node1') const node1 = document.createTextNode('node1')
const node2 = document.createTextNode('node2') const node2 = document.createTextNode('node2')
const node3 = document.createTextNode('node3') const node3 = document.createTextNode('node3')
const anchor = document.createTextNode('anchor') const anchor = document.createTextNode('anchor')
describe('dom', () => { describe('element', () => {
test('normalizeBlock', () => { test('normalizeBlock', () => {
expect(normalizeBlock([node1, node2, node3])).toEqual([node1, node2, node3]) expect(normalizeBlock([node1, node2, node3])).toEqual([node1, node2, node3])
expect(normalizeBlock([node1, [node2, [node3]]])).toEqual([ expect(normalizeBlock([node1, [node2, [node3]]])).toEqual([

View File

@ -13,7 +13,7 @@ import {
createComponentInstance, createComponentInstance,
setCurrentInstance, setCurrentInstance,
} from '../../src/component' } from '../../src/component'
import { MetadataKind, getMetadata, recordMetadata } from '../../src/metadata' import { getMetadata, recordPropMetadata } from '../../src/metadata'
let removeComponentInstance = NOOP let removeComponentInstance = NOOP
beforeEach(() => { beforeEach(() => {
@ -30,16 +30,16 @@ afterEach(() => {
}) })
describe('patchProp', () => { describe('patchProp', () => {
describe('recordMetadata', () => { describe('recordPropMetadata', () => {
test('should record prop metadata', () => { test('should record prop metadata', () => {
const node = {} as Node // the node is just a key const node = {} as Node // the node is just a key
let prev = recordMetadata(node, MetadataKind.prop, 'class', 'foo') let prev = recordPropMetadata(node, 'class', 'foo')
expect(prev).toBeUndefined() expect(prev).toBeUndefined()
prev = recordMetadata(node, MetadataKind.prop, 'class', 'bar') prev = recordPropMetadata(node, 'class', 'bar')
expect(prev).toBe('foo') expect(prev).toBe('foo')
prev = recordMetadata(node, MetadataKind.prop, 'style', 'color: red') prev = recordPropMetadata(node, 'style', 'color: red')
expect(prev).toBeUndefined() expect(prev).toBeUndefined()
prev = recordMetadata(node, MetadataKind.prop, 'style', 'color: blue') prev = recordPropMetadata(node, 'style', 'color: blue')
expect(prev).toBe('color: red') expect(prev).toBe('color: red')
expect(getMetadata(node)).toEqual([ expect(getMetadata(node)).toEqual([
@ -51,8 +51,8 @@ describe('patchProp', () => {
test('should have different metadata for different nodes', () => { test('should have different metadata for different nodes', () => {
const node1 = {} as Node const node1 = {} as Node
const node2 = {} as Node const node2 = {} as Node
recordMetadata(node1, MetadataKind.prop, 'class', 'foo') recordPropMetadata(node1, 'class', 'foo')
recordMetadata(node2, MetadataKind.prop, 'class', 'bar') recordPropMetadata(node2, 'class', 'bar')
expect(getMetadata(node1)).toEqual([{ class: 'foo' }, {}]) expect(getMetadata(node1)).toEqual([{ class: 'foo' }, {}])
expect(getMetadata(node2)).toEqual([{ class: 'bar' }, {}]) expect(getMetadata(node2)).toEqual([{ class: 'bar' }, {}])
}) })

View File

@ -4,7 +4,7 @@ import {
onEffectCleanup, onEffectCleanup,
onScopeDispose, onScopeDispose,
} from '@vue/reactivity' } from '@vue/reactivity'
import { MetadataKind, getMetadata, recordMetadata } from '../metadata' import { MetadataKind, getMetadata, recordEventMetadata } from '../metadata'
import { withKeys, withModifiers } from '@vue/runtime-dom' import { withKeys, withModifiers } from '@vue/runtime-dom'
export function addEventListener( export function addEventListener(
@ -29,12 +29,16 @@ export function on(
options: AddEventListenerOptions & ModifierOptions = {}, options: AddEventListenerOptions & ModifierOptions = {},
) { ) {
const handler: DelegatedHandler = eventHandler(handlerGetter, options) const handler: DelegatedHandler = eventHandler(handlerGetter, options)
recordMetadata(el, MetadataKind.event, event, handler) const cleanupMetadata = recordEventMetadata(el, event, handler)
const cleanupEvent = addEventListener(el, event, handler, options)
function cleanup() {
cleanupMetadata()
cleanupEvent()
}
const cleanup = addEventListener(el, event, handler, options)
const scope = getCurrentScope() const scope = getCurrentScope()
const effect = getCurrentEffect() const effect = getCurrentEffect()
if (effect && effect.scope === scope) { if (effect && effect.scope === scope) {
onEffectCleanup(cleanup) onEffectCleanup(cleanup)
} else if (scope) { } else if (scope) {
@ -55,7 +59,7 @@ export function delegate(
) { ) {
const handler: DelegatedHandler = eventHandler(handlerGetter, options) const handler: DelegatedHandler = eventHandler(handlerGetter, options)
handler.delegate = true handler.delegate = true
recordMetadata(el, MetadataKind.event, event, handler) recordEventMetadata(el, event, handler)
} }
function eventHandler( function eventHandler(
@ -103,10 +107,14 @@ const delegatedEventHandler = (e: Event) => {
}, },
}) })
while (node !== null) { while (node !== null) {
const handler = getMetadata(node)[MetadataKind.event][e.type] const handlers = getMetadata(node)[MetadataKind.event][e.type]
if (handler && handler.delegate && !node.disabled) { if (handlers) {
handler(e) for (const handler of handlers) {
if (e.cancelBubble) return if (handler.delegate && !node.disabled) {
handler(e)
if (e.cancelBubble) return
}
}
} }
node = node =
node.host && node.host !== node && node.host instanceof Node node.host && node.host !== node && node.host instanceof Node

View File

@ -11,22 +11,17 @@ import {
} from '@vue/shared' } from '@vue/shared'
import { warn } from '../warning' import { warn } from '../warning'
import { setStyle } from './style' import { setStyle } from './style'
import { MetadataKind, getMetadata, recordMetadata } from '../metadata' import { MetadataKind, getMetadata, recordPropMetadata } from '../metadata'
export function setClass(el: Element, value: any) { export function setClass(el: Element, value: any) {
const prev = recordMetadata( const prev = recordPropMetadata(el, 'class', (value = normalizeClass(value)))
el,
MetadataKind.prop,
'class',
(value = normalizeClass(value)),
)
if (value !== prev && (value || prev)) { if (value !== prev && (value || prev)) {
el.className = value el.className = value
} }
} }
export function setAttr(el: Element, key: string, value: any) { export function setAttr(el: Element, key: string, value: any) {
const oldVal = recordMetadata(el, MetadataKind.prop, key, value) const oldVal = recordPropMetadata(el, key, value)
if (value !== oldVal) { if (value !== oldVal) {
if (value != null) { if (value != null) {
el.setAttribute(key, value) el.setAttribute(key, value)
@ -37,7 +32,7 @@ export function setAttr(el: Element, key: string, value: any) {
} }
export function setDOMProp(el: any, key: string, value: any) { export function setDOMProp(el: any, key: string, value: any) {
const oldVal = recordMetadata(el, MetadataKind.prop, key, value) const oldVal = recordPropMetadata(el, key, value)
if (value === oldVal) return if (value === oldVal) return
if (key === 'innerHTML' || key === 'textContent') { if (key === 'innerHTML' || key === 'textContent') {
@ -184,14 +179,14 @@ function mergeProps(...args: Data[]) {
export function setText(el: Node, ...values: any[]) { export function setText(el: Node, ...values: any[]) {
const text = values.map(v => toDisplayString(v)).join('') const text = values.map(v => toDisplayString(v)).join('')
const oldVal = recordMetadata(el, MetadataKind.prop, 'textContent', text) const oldVal = recordPropMetadata(el, 'textContent', text)
if (text !== oldVal) { if (text !== oldVal) {
el.textContent = text el.textContent = text
} }
} }
export function setHtml(el: Element, value: any) { export function setHtml(el: Element, value: any) {
const oldVal = recordMetadata(el, MetadataKind.prop, 'innerHTML', value) const oldVal = recordPropMetadata(el, 'innerHTML', value)
if (value !== oldVal) { if (value !== oldVal) {
el.innerHTML = value el.innerHTML = value
} }

View File

@ -7,15 +7,10 @@ import {
normalizeStyle, normalizeStyle,
} from '@vue/shared' } from '@vue/shared'
import { warn } from '../warning' import { warn } from '../warning'
import { MetadataKind, recordMetadata } from '../metadata' import { recordPropMetadata } from '../metadata'
export function setStyle(el: HTMLElement, value: any) { export function setStyle(el: HTMLElement, value: any) {
const prev = recordMetadata( const prev = recordPropMetadata(el, 'style', (value = normalizeStyle(value)))
el,
MetadataKind.prop,
'style',
(value = normalizeStyle(value)),
)
patchStyle(el, prev, value) patchStyle(el, prev, value)
} }

View File

@ -1,4 +1,4 @@
import type { Data } from '@vue/shared' import { type Data, remove } from '@vue/shared'
import type { DelegatedHandler } from './dom/event' import type { DelegatedHandler } from './dom/event'
export enum MetadataKind { export enum MetadataKind {
@ -8,7 +8,7 @@ export enum MetadataKind {
export type ElementMetadata = [ export type ElementMetadata = [
props: Data, props: Data,
events: Record<string, DelegatedHandler>, events: Record<string, DelegatedHandler[]>,
] ]
export function getMetadata( export function getMetadata(
@ -17,14 +17,16 @@ export function getMetadata(
return el.$$metadata || (el.$$metadata = [{}, {}]) return el.$$metadata || (el.$$metadata = [{}, {}])
} }
export function recordMetadata( export function recordPropMetadata(el: Node, key: string, value: any): any {
el: Node, const metadata = getMetadata(el)[MetadataKind.prop]
kind: MetadataKind,
key: string,
value: any,
): any {
const metadata = getMetadata(el)[kind]
const prev = metadata[key] const prev = metadata[key]
metadata[key] = value metadata[key] = value
return prev return prev
} }
export function recordEventMetadata(el: Node, key: string, value: any) {
const metadata = getMetadata(el)[MetadataKind.event]
const handlers = (metadata[key] ||= [])
handlers.push(value)
return () => remove(handlers, value)
}