mirror of https://github.com/vuejs/core.git
143 lines
3.6 KiB
TypeScript
143 lines
3.6 KiB
TypeScript
import { camelize, isArray, normalizeClass, normalizeStyle } from '@vue/shared'
|
|
import { type ComponentInternalInstance, currentInstance } from './component'
|
|
import { isEmitListener } from './componentEmits'
|
|
import { type RawProps, walkRawProps } from './componentProps'
|
|
import { renderEffect } from './renderEffect'
|
|
import { mergeProp, setDynamicProp } from './dom/prop'
|
|
|
|
export function patchAttrs(
|
|
instance: ComponentInternalInstance,
|
|
hasDynamicProps?: boolean,
|
|
): void {
|
|
const {
|
|
attrs,
|
|
rawProps,
|
|
propsOptions: [options],
|
|
} = instance
|
|
|
|
if (!rawProps.length) return
|
|
const keys = new Set<string>()
|
|
const classes: any[] = []
|
|
const styles: any[] = []
|
|
|
|
walkRawProps(rawProps, registerAttr)
|
|
for (const key in attrs) {
|
|
if (!keys.has(key)) {
|
|
delete attrs[key]
|
|
}
|
|
}
|
|
|
|
setClassOrStyle(classes, 'class', normalizeClass)
|
|
setClassOrStyle(styles, 'style', normalizeStyle)
|
|
|
|
function setClassOrStyle(
|
|
values: any[],
|
|
field: 'class' | 'style',
|
|
normalize: (value: any) => any,
|
|
) {
|
|
if (values.length) {
|
|
if (hasDynamicProps) {
|
|
Object.defineProperty(attrs, field, {
|
|
get() {
|
|
return normalize(values.map(value => value()))
|
|
},
|
|
enumerable: true,
|
|
configurable: true,
|
|
})
|
|
} else {
|
|
attrs[field] = normalizeClass(values)
|
|
}
|
|
}
|
|
}
|
|
|
|
function registerAttr(key: string, value: any, getter?: boolean) {
|
|
if (
|
|
(!options || !(camelize(key) in options)) &&
|
|
!isEmitListener(instance.emitsOptions, key) &&
|
|
(key === 'class' || key === 'style' || !keys.has(key))
|
|
) {
|
|
keys.add(key)
|
|
|
|
if (key === 'class' || key === 'style') {
|
|
;(key === 'class' ? classes : styles).push(
|
|
hasDynamicProps
|
|
? getter
|
|
? value
|
|
: () => value
|
|
: getter
|
|
? value()
|
|
: value,
|
|
)
|
|
} else if (getter) {
|
|
Object.defineProperty(attrs, key, {
|
|
get: value,
|
|
enumerable: true,
|
|
configurable: true,
|
|
})
|
|
} else {
|
|
attrs[key] = value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export function withAttrs(props: RawProps): RawProps {
|
|
const instance = currentInstance!
|
|
if (instance.type.inheritAttrs === false) return props
|
|
const attrsGetter = () => instance.attrs
|
|
if (!props) return [attrsGetter]
|
|
if (isArray(props)) {
|
|
return [attrsGetter, ...props]
|
|
}
|
|
return [attrsGetter, props]
|
|
}
|
|
|
|
export function fallThroughAttrs(
|
|
instance: ComponentInternalInstance,
|
|
element: Element,
|
|
): void {
|
|
const {
|
|
type: { inheritAttrs },
|
|
dynamicAttrs,
|
|
} = instance
|
|
if (
|
|
inheritAttrs === false ||
|
|
dynamicAttrs === true // all props as dynamic
|
|
)
|
|
return
|
|
|
|
const hasStaticAttrs = dynamicAttrs || dynamicAttrs === false
|
|
|
|
let initial: Record<string, string> | undefined
|
|
if (hasStaticAttrs) {
|
|
// attrs in static template
|
|
initial = {}
|
|
for (let i = 0; i < element.attributes.length; i++) {
|
|
const attr = element.attributes[i]
|
|
if (dynamicAttrs && dynamicAttrs.includes(attr.name)) continue
|
|
initial[attr.name] = attr.value
|
|
}
|
|
}
|
|
|
|
renderEffect(() => {
|
|
for (const key in instance.attrs) {
|
|
if (dynamicAttrs && dynamicAttrs.includes(key)) continue
|
|
|
|
let value: unknown
|
|
if (hasStaticAttrs && key in initial!) {
|
|
value = mergeProp(key, instance.attrs[key], initial![key])
|
|
} else {
|
|
value = instance.attrs[key]
|
|
}
|
|
|
|
setDynamicProp(element, key, value)
|
|
}
|
|
})
|
|
}
|
|
|
|
export function setInheritAttrs(dynamicAttrs?: string[] | boolean): void {
|
|
const instance = currentInstance!
|
|
if (instance.type.inheritAttrs === false) return
|
|
instance.dynamicAttrs = dynamicAttrs
|
|
}
|