]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: style/class normalization
authorEvan You <yyx990803@gmail.com>
Sat, 1 Jun 2019 09:43:41 +0000 (17:43 +0800)
committerEvan You <yyx990803@gmail.com>
Sat, 1 Jun 2019 09:43:41 +0000 (17:43 +0800)
packages/runtime-core/src/componentProps.ts
packages/runtime-core/src/createRenderer.ts
packages/runtime-core/src/vnode.ts

index 7e4fac1051fe2633111ed01f39f1168341421ecb..77c07c651b6bc00ab23defd2975c9e5a2f71d62c 100644 (file)
@@ -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)
index 636af1155fd4db1290de090b7a338c0105a30077..de0a3d8e53728855ee6724fcd432821b5aee35fa 100644 (file)
@@ -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
index d9fc82fb9c494df6e6005cdc75f71ca0f9e03140..ff4ea11675411fee6b29c538774b71175aa34b6e 100644 (file)
@@ -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()
+}