type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>
type InferPropType<T> = T extends null
- ? any
- : // null & true would fail to infer
- T extends { type: null | true }
- ? any
- : // somehow `ObjectContructor` when inferred from { (): T } becomes `any`
- T extends ObjectConstructor | { type: ObjectConstructor }
+ ? any // null & true would fail to infer
+ : T extends { type: null | true }
+ ? any // somehow `ObjectContructor` when inferred from { (): T } becomes `any`
+ : T extends ObjectConstructor | { type: ObjectConstructor }
? { [key: string]: any }
: T extends Prop<infer V> ? V : T
type NormalizedPropsOptions = Record<string, NormalizedProp>
-const isReservedKey = (key: string): boolean => key[0] === '_' || key[0] === '$'
-
// resolve raw VNode data.
// - filter out reserved keys (key, ref, slots)
// - extract class and style into $attrs (to be merged onto child
warn(`props must be strings when using array syntax.`, raw[i])
}
const normalizedKey = camelize(raw[i])
- if (!isReservedKey(normalizedKey)) {
+ if (normalizedKey[0] !== '$') {
normalized[normalizedKey] = EMPTY_OBJ
} else if (__DEV__) {
warn(`Invalid prop name: "${normalizedKey}" is a reserved property.`)
}
for (const key in raw) {
const normalizedKey = camelize(key)
- if (!isReservedKey(normalizedKey)) {
+ if (normalizedKey[0] !== '$') {
const opt = raw[key]
const prop: NormalizedProp = (normalized[normalizedKey] =
isArray(opt) || isFunction(opt) ? { type: opt } : opt)
const oldProps = (n1 && n1.props) || EMPTY_OBJ
const newProps = n2.props || EMPTY_OBJ
- if (patchFlag != null) {
+ if (patchFlag) {
// the presence of a patchFlag means this element's render code was
// generated by the compiler and can take the fast path.
// in this path old node and new node are guaranteed to have the same shape
// class
// this flag is matched when the element has dynamic class bindings.
if (patchFlag & CLASS) {
- // TODO handle full class API, potentially optimize at compilation stage?
if (oldProps.class !== newProps.class) {
hostPatchProp(el, 'class', newProps.class, null, false)
}
) {
if (oldProps !== newProps) {
for (const key in newProps) {
+ if (key === 'key' || key === 'ref') continue
const next = newProps[key]
const prev = oldProps[key]
if (next !== prev) {
}
if (oldProps !== EMPTY_OBJ) {
for (const key in oldProps) {
+ if (key === 'key' || key === 'ref') continue
if (!(key in newProps)) {
hostPatchProp(
el,
// fast path
const { patchFlag } = n2
- if (patchFlag != null) {
+ if (patchFlag) {
if (patchFlag & KEYED) {
// this could be either fully-keyed or mixed (some keyed some not)
// presence of patchFlag means children are guaranteed to be arrays
import { ComponentInstance } from './component'
import { HostNode } from './createRenderer'
import { RawSlots } from './componentSlots'
+import { CLASS, STYLE } from './patchFlags'
export const Fragment = Symbol('Fragment')
export const Text = Symbol('Text')
target: HostNode | null // portal target
// optimization only
- patchFlag: number | null
+ patchFlag: number
dynamicProps: string[] | null
dynamicChildren: VNode[] | null
}
type: VNodeTypes,
props: { [key: string]: any } | null | 0 = null,
children: any = null,
- patchFlag: number | null = null,
+ patchFlag: number = 0,
dynamicProps: string[] | null = null
): VNode {
+ // Allow passing 0 for props, this can save bytes on generated code.
+ props = props || null
const vnode: VNode = {
type,
- props: props || null,
+ props,
key: props && props.key,
children: normalizeChildren(children),
component: null,
dynamicProps,
dynamicChildren: null
}
+
+ // class & style normalization.
+ if (props !== null) {
+ // class normalization only needed if the vnode isn't generated by
+ // compiler-optimized code
+ if (props.class != null && !(patchFlag & CLASS)) {
+ props.class = normalizeClass(props.class)
+ }
+ if (props.style != null) {
+ props.style = normalizeStyle(props.style)
+ }
+ }
+
// presence of a patch flag indicates this node is dynamic
// component nodes also should always be tracked, because even if the
// component doesn't need to update, it needs to persist the instance on to
// the next vnode so that it can be properly unmounted later.
- if (
- shouldTrack &&
- (patchFlag != null || isObject(type) || isFunction(type))
- ) {
+ if (shouldTrack && (patchFlag || isObject(type) || isFunction(type))) {
trackDynamicNode(vnode)
}
+
return vnode
}
return isString(children) ? children : children + ''
}
}
+
+function normalizeStyle(
+ value: unknown
+): Record<string, string | number> | void {
+ if (isArray(value)) {
+ const res: Record<string, string | number> = {}
+ for (let i = 0; i < value.length; i++) {
+ const normalized = normalizeStyle(value[i])
+ if (normalized) {
+ for (const key in normalized) {
+ res[key] = normalized[key]
+ }
+ }
+ }
+ return res
+ } else if (isObject(value)) {
+ return value
+ }
+}
+
+function normalizeClass(value: unknown): string {
+ let res = ''
+ if (isString(value)) {
+ res = value
+ } else if (isArray(value)) {
+ for (let i = 0; i < value.length; i++) {
+ res += normalizeClass(value[i]) + ' '
+ }
+ } else if (isObject(value)) {
+ for (const name in value) {
+ if (value[name]) {
+ res += name + ' '
+ }
+ }
+ }
+ return res.trim()
+}