wip: style/class normalization
This commit is contained in:
parent
05556eacb2
commit
d6d4ba8679
@ -36,12 +36,10 @@ type RequiredKeys<T> = {
|
||||
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
|
||||
|
||||
@ -64,8 +62,6 @@ type NormalizedProp =
|
||||
|
||||
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
|
||||
@ -182,7 +178,7 @@ function normalizePropsOptions(
|
||||
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.`)
|
||||
@ -194,7 +190,7 @@ function normalizePropsOptions(
|
||||
}
|
||||
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)
|
||||
|
@ -226,7 +226,7 @@ export function createRenderer(options: RendererOptions) {
|
||||
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
|
||||
@ -239,7 +239,6 @@ export function createRenderer(options: RendererOptions) {
|
||||
// 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)
|
||||
}
|
||||
@ -314,6 +313,7 @@ export function createRenderer(options: RendererOptions) {
|
||||
) {
|
||||
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) {
|
||||
@ -330,6 +330,7 @@ export function createRenderer(options: RendererOptions) {
|
||||
}
|
||||
if (oldProps !== EMPTY_OBJ) {
|
||||
for (const key in oldProps) {
|
||||
if (key === 'key' || key === 'ref') continue
|
||||
if (!(key in newProps)) {
|
||||
hostPatchProp(
|
||||
el,
|
||||
@ -521,7 +522,7 @@ export function createRenderer(options: RendererOptions) {
|
||||
|
||||
// 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
|
||||
|
@ -2,6 +2,7 @@ import { isArray, isFunction, isString, isObject, EMPTY_ARR } from '@vue/shared'
|
||||
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')
|
||||
@ -35,7 +36,7 @@ export interface VNode {
|
||||
target: HostNode | null // portal target
|
||||
|
||||
// optimization only
|
||||
patchFlag: number | null
|
||||
patchFlag: number
|
||||
dynamicProps: string[] | null
|
||||
dynamicChildren: VNode[] | null
|
||||
}
|
||||
@ -87,12 +88,14 @@ export function createVNode(
|
||||
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,
|
||||
@ -103,16 +106,27 @@ export function createVNode(
|
||||
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
|
||||
}
|
||||
|
||||
@ -158,3 +172,40 @@ export function normalizeChildren(children: unknown): NormalizedChildren {
|
||||
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()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user