]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: new attrs merge strategy
authorEvan You <yyx990803@gmail.com>
Mon, 24 Sep 2018 23:11:14 +0000 (19:11 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 24 Sep 2018 23:11:14 +0000 (19:11 -0400)
packages/core/src/component.ts
packages/core/src/componentUtils.ts
packages/core/src/h.ts
packages/core/src/utils.ts
packages/core/src/vdom.ts
packages/renderer-dom/src/modules/class.ts
packages/renderer-dom/src/modules/style.ts

index 376d28fc69d46fecc964bc42e53c628641dc7aa4..34ef7e51335301df69213dad8c355cbe88868c57 100644 (file)
@@ -37,7 +37,7 @@ export interface MountedComponent<D = Data, P = Data> extends Component {
   $children: MountedComponent[]
   $options: ComponentOptions<D, P>
 
-  render(props: P, slots: Slots): any
+  render(props: P, slots: Slots, attrs: Data): any
   renderError?(e: Error): any
   renderTracked?(e: DebuggerEvent): void
   renderTriggered?(e: DebuggerEvent): void
index 4ad27fd7403ff47bb270fec0e1b55c215b002bb1..33e54b391c70bca08eb0457972d37949387e2802 100644 (file)
@@ -59,7 +59,8 @@ export function renderInstanceRoot(instance: MountedComponent) {
     vnode = instance.render.call(
       instance.$proxy,
       instance.$props,
-      instance.$slots
+      instance.$slots,
+      instance.$attrs
     )
   } catch (e1) {
     handleError(e1, instance, ErrorTypes.RENDER)
@@ -105,17 +106,13 @@ export function normalizeComponentRoot(
     vnode = createFragment(vnode)
   } else {
     const { flags } = vnode
-    // parentVNode data merge down
     if (
       componentVNode &&
       (flags & VNodeFlags.COMPONENT || flags & VNodeFlags.ELEMENT)
     ) {
       if (inheritAttrs !== false && attrs !== void 0) {
-        // TODO should merge
-        console.log(attrs)
         vnode = cloneVNode(vnode, attrs)
-      }
-      if (vnode.el) {
+      } else if (vnode.el) {
         vnode = cloneVNode(vnode)
       }
       if (flags & VNodeFlags.COMPONENT) {
index be913246e1a241f79de472303b3aa7bfa92b4c1a..6888207b9ebba26b85737af744d542ced34279a6 100644 (file)
@@ -9,6 +9,7 @@ import {
   createFragment,
   createPortal
 } from './vdom'
+import { isObservable } from '@vue/observer'
 
 export const Fragment = Symbol()
 export const Portal = Symbol()
@@ -36,7 +37,15 @@ export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
     data = null
   }
 
-  // TODO clone data if it is observed
+  if (data === void 0) data = null
+  if (children === void 0) children = null
+
+  if (__DEV__ && isObservable(data)) {
+    console.warn(
+      `Do not used observed state as VNode data - always create fresh objects.`,
+      data
+    )
+  }
 
   let key = null
   let ref = null
index e0605d5d64d71a0efcb7bbfd7f23ce907d3f240f..7ea9151cda206944a4c317743192f7fe9b174352 100644 (file)
@@ -11,6 +11,43 @@ export const isReservedProp = (key: string): boolean => {
   }
 }
 
+export function normalizeStyle(
+  value: any
+): Record<string, string | number> | void {
+  if (Array.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 (value && typeof value === 'object') {
+    return value
+  }
+}
+
+export function normalizeClass(value: any): string {
+  let res = ''
+  if (typeof value === 'string') {
+    res = value
+  } else if (Array.isArray(value)) {
+    for (let i = 0; i < value.length; i++) {
+      res += normalizeClass(value[i]) + ' '
+    }
+  } else if (typeof value === 'object') {
+    for (const name in value) {
+      if (value[name]) {
+        res += name + ' '
+      }
+    }
+  }
+  return res.trim()
+}
+
 // https://en.wikipedia.org/wiki/Longest_increasing_subsequence
 export function lis(arr: number[]): number[] {
   const p = arr.slice()
index da139d29210eb21c19d20aae3e9ce72dd13e7c1e..c3481d9d916d7d7d1c526cb76b22d88af4b75a46 100644 (file)
@@ -5,6 +5,7 @@ import {
 } from './component'
 import { VNodeFlags, ChildrenFlags } from './flags'
 import { createComponentClassFromOptions } from './componentUtils'
+import { normalizeClass, normalizeStyle } from './utils'
 
 // Vue core is platform agnostic, so we are not using Element for "DOM" nodes.
 export interface RenderNode {
@@ -95,6 +96,15 @@ export function createVNode(
   return vnode
 }
 
+function normalizeClassAndStyle(data: VNodeData) {
+  if (data.class != null) {
+    data.class = normalizeClass(data.class)
+  }
+  if (data.style != null) {
+    data.style = normalizeStyle(data.style)
+  }
+}
+
 export function createElementVNode(
   tag: string,
   data: VNodeData | null,
@@ -104,6 +114,9 @@ export function createElementVNode(
   ref?: Ref | null
 ) {
   const flags = tag === 'svg' ? VNodeFlags.ELEMENT_SVG : VNodeFlags.ELEMENT_HTML
+  if (data !== null) {
+    normalizeClassAndStyle(data)
+  }
   return createVNode(flags, tag, data, children, childFlags, key, ref, null)
 }
 
@@ -175,6 +188,11 @@ export function createComponentVNode(
     }
   }
 
+  // class & style
+  if (data !== null) {
+    normalizeClassAndStyle(data)
+  }
+
   return createVNode(
     flags,
     comp,
@@ -248,7 +266,18 @@ export function cloneVNode(vnode: VNode, extraData?: VNodeData): VNode {
         }
       }
       for (const key in extraData) {
-        clonedData[key] = extraData[key]
+        if (key === 'class') {
+          clonedData.class = normalizeClass([clonedData.class, extraData.class])
+        } else if (key === 'style') {
+          clonedData.style = normalizeStyle([clonedData.style, extraData.style])
+        } else if (key.startsWith('on')) {
+          const existing = clonedData[key]
+          clonedData[key] = existing
+            ? [].concat(existing, extraData[key])
+            : extraData[key]
+        } else {
+          clonedData[key] = extraData[key]
+        }
       }
     }
     return createVNode(
index f953d46f693e72463b0ed9e29aff9a006cb3b16d..ff5b90238a986a6f03ca663f1f6b7ea53d7d61a0 100644 (file)
@@ -1,29 +1,11 @@
 // compiler should normlaize class + :class bindings on the same element
 // into a single binding ['staticClass', dynamic]
 
-export function patchClass(el: Element, value: any, isSVG: boolean) {
+export function patchClass(el: Element, value: string, isSVG: boolean) {
   // directly setting className should be faster than setAttribute in theory
   if (isSVG) {
-    el.setAttribute('class', normalizeClass(value))
+    el.setAttribute('class', value)
   } else {
-    el.className = normalizeClass(value)
+    el.className = value
   }
 }
-
-function normalizeClass(value: any): string {
-  let res = ''
-  if (typeof value === 'string') {
-    res = value
-  } else if (Array.isArray(value)) {
-    for (let i = 0; i < value.length; i++) {
-      res += normalizeClass(value[i]) + ' '
-    }
-  } else if (typeof value === 'object') {
-    for (const name in value) {
-      if (value[name]) {
-        res += name + ' '
-      }
-    }
-  }
-  return res.trim()
-}
index bdf876b7f36fbb107a03ed20a1c2270dfe3cd0a8..f347064b4f8993a77a3e2f3cc342b142ed3f679f 100644 (file)
@@ -1,5 +1,3 @@
-import { isObservable } from '@vue/core'
-
 // style properties that should NOT have "px" added when numeric
 const nonNumericRE = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i
 
@@ -10,44 +8,19 @@ export function patchStyle(el: any, prev: any, next: any, data: any) {
   } else if (typeof next === 'string') {
     style.cssText = next
   } else {
-    // TODO: warn invalid value in dev
-    const normalizedNext: any = normalizeStyle(next)
-    // If next is observed, the user is likely to mutate the style object.
-    // We need to replace data.style with the normalized clone.
-    if (isObservable(next)) {
-      data.style = normalizedNext
-    }
-    for (const key in normalizedNext) {
-      let value = normalizedNext[key]
+    for (const key in next) {
+      let value = next[key]
       if (typeof value === 'number' && !nonNumericRE.test(key)) {
         value = value + 'px'
       }
       style[key] = value
     }
     if (prev && typeof prev !== 'string') {
-      prev = normalizeStyle(prev)
       for (const key in prev) {
-        if (!normalizedNext[key]) {
+        if (!next[key]) {
           style[key] = ''
         }
       }
     }
   }
 }
-
-function normalizeStyle(value: any): Record<string, string | number> | void {
-  if (value && typeof value === 'object') {
-    return value
-  } else if (Array.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
-  }
-}