refactor(runtime-vapor): split props & event metadata

This commit is contained in:
三咲智子 Kevin Deng 2024-02-23 13:21:04 +08:00
parent 88123e56d0
commit 23653cc447
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
6 changed files with 61 additions and 41 deletions

View File

@ -13,7 +13,7 @@ import {
createComponentInstance, createComponentInstance,
setCurrentInstance, setCurrentInstance,
} from '../../src/component' } from '../../src/component'
import { getMetadata, recordPropMetadata } from '../../src/metadata' import { getMetadata, recordMetadata } from '../../src/metadata'
let removeComponentInstance = NOOP let removeComponentInstance = NOOP
beforeEach(() => { beforeEach(() => {
@ -30,33 +30,36 @@ afterEach(() => {
}) })
describe('patchProp', () => { describe('patchProp', () => {
describe('recordPropMetadata', () => { describe('recordMetadata', () => {
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 = recordPropMetadata(node, 'class', 'foo') let prev = recordMetadata(node, 'props', 'class', 'foo')
expect(prev).toBeUndefined() expect(prev).toBeUndefined()
prev = recordPropMetadata(node, 'class', 'bar') prev = recordMetadata(node, 'props', 'class', 'bar')
expect(prev).toBe('foo') expect(prev).toBe('foo')
prev = recordPropMetadata(node, 'style', 'color: red') prev = recordMetadata(node, 'props', 'style', 'color: red')
expect(prev).toBeUndefined() expect(prev).toBeUndefined()
prev = recordPropMetadata(node, 'style', 'color: blue') prev = recordMetadata(node, 'props', 'style', 'color: blue')
expect(prev).toBe('color: red') expect(prev).toBe('color: red')
expect(getMetadata(node)).toEqual({ expect(getMetadata(node)).toEqual({
props: { class: 'bar', style: 'color: blue' }, props: { class: 'bar', style: 'color: blue' },
events: {},
}) })
}) })
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
recordPropMetadata(node1, 'class', 'foo') recordMetadata(node1, 'props', 'class', 'foo')
recordPropMetadata(node2, 'class', 'bar') recordMetadata(node2, 'props', 'class', 'bar')
expect(getMetadata(node1)).toEqual({ expect(getMetadata(node1)).toEqual({
props: { class: 'foo' }, props: { class: 'foo' },
events: {},
}) })
expect(getMetadata(node2)).toEqual({ expect(getMetadata(node2)).toEqual({
props: { class: 'bar' }, props: { class: 'bar' },
events: {},
}) })
}) })
}) })

View File

@ -20,7 +20,7 @@ import { getMetadata } from '../metadata'
type AssignerFn = (value: any) => void type AssignerFn = (value: any) => void
function getModelAssigner(el: Element): AssignerFn { function getModelAssigner(el: Element): AssignerFn {
const metadata = getMetadata(el) const metadata = getMetadata(el)
const fn: any = metadata.props['onUpdate:modelValue'] const fn: any = metadata.events['update:modelValue']
return isArray(fn) ? value => invokeArrayFns(fn, value) : fn return isArray(fn) ? value => invokeArrayFns(fn, value) : fn
} }

View File

@ -4,8 +4,7 @@ import {
onEffectCleanup, onEffectCleanup,
onScopeDispose, onScopeDispose,
} from '@vue/reactivity' } from '@vue/reactivity'
import { recordPropMetadata } from '../metadata' import { recordMetadata } from '../metadata'
import { toHandlerKey } from '@vue/shared'
import { withKeys, withModifiers } from '@vue/runtime-dom' import { withKeys, withModifiers } from '@vue/runtime-dom'
export function addEventListener( export function addEventListener(
@ -25,11 +24,7 @@ export function on(
options?: AddEventListenerOptions, options?: AddEventListenerOptions,
{ modifiers, keys }: { modifiers?: string[]; keys?: string[] } = {}, { modifiers, keys }: { modifiers?: string[]; keys?: string[] } = {},
) { ) {
recordPropMetadata(el, toHandlerKey(event), handlerGetter) const handler = (...args: any[]) => {
const cleanup = addEventListener(
el,
event,
(...args: any[]) => {
let handler = handlerGetter() let handler = handlerGetter()
if (!handler) return if (!handler) return
@ -40,9 +35,9 @@ export function on(
handler = withKeys(handler, keys) handler = withKeys(handler, keys)
} }
handler && handler(...args) handler && handler(...args)
}, }
options, recordMetadata(el, 'events', event, handler)
) const cleanup = addEventListener(el, event, handler, options)
const scope = getCurrentScope() const scope = getCurrentScope()
const effect = getCurrentEffect() const effect = getCurrentEffect()

View File

@ -11,17 +11,22 @@ import {
} from '@vue/shared' } from '@vue/shared'
import { warn } from '../warning' import { warn } from '../warning'
import { setStyle } from './style' import { setStyle } from './style'
import { getMetadata, recordPropMetadata } from '../metadata' import { getMetadata, recordMetadata } from '../metadata'
export function setClass(el: Element, value: any) { export function setClass(el: Element, value: any) {
const prev = recordPropMetadata(el, 'class', (value = normalizeClass(value))) const prev = recordMetadata(
el,
'props',
'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 = recordPropMetadata(el, key, value) const oldVal = recordMetadata(el, 'props', key, value)
if (value !== oldVal) { if (value !== oldVal) {
if (value != null) { if (value != null) {
el.setAttribute(key, value) el.setAttribute(key, value)
@ -32,7 +37,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 = recordPropMetadata(el, key, value) const oldVal = recordMetadata(el, 'props', key, value)
if (value === oldVal) return if (value === oldVal) return
if (key === 'innerHTML' || key === 'textContent') { if (key === 'innerHTML' || key === 'textContent') {
@ -179,14 +184,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 = recordPropMetadata(el, 'textContent', text) const oldVal = recordMetadata(el, 'props', '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 = recordPropMetadata(el, 'innerHTML', value) const oldVal = recordMetadata(el, 'props', 'innerHTML', value)
if (value !== oldVal) { if (value !== oldVal) {
el.innerHTML = value el.innerHTML = value
} }

View File

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

View File

@ -2,17 +2,29 @@ import type { Data } from '@vue/shared'
export interface ElementMetadata { export interface ElementMetadata {
props: Data props: Data
events: Data
} }
export function getMetadata( export function getMetadata(
el: Node & { $$metadata?: ElementMetadata }, el: Node & { $$metadata?: ElementMetadata },
): ElementMetadata { ): ElementMetadata {
return el.$$metadata || (el.$$metadata = { props: {} }) return (
el.$$metadata ||
(el.$$metadata = {
props: {},
events: {},
})
)
} }
export function recordPropMetadata(el: Node, key: string, value: any): any { export function recordMetadata(
const metadata = getMetadata(el) el: Node,
const prev = metadata.props[key] kind: 'props' | 'events',
metadata.props[key] = value key: string,
value: any,
): any {
const metadata = getMetadata(el)[kind]
const prev = metadata[key]
metadata[key] = value
return prev return prev
} }