]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: shouldUpdateComponent logic for slots
authorEvan You <yyx990803@gmail.com>
Fri, 31 May 2019 04:25:11 +0000 (12:25 +0800)
committerEvan You <yyx990803@gmail.com>
Fri, 31 May 2019 04:25:11 +0000 (12:25 +0800)
packages/runtime-core/src/component.ts
packages/runtime-core/src/createRenderer.ts
packages/runtime-core/src/patchFlags.ts

index af5af2292d750f406b99531e47d6abd90d0c8afd..496ee37c1f0ffbadaa9b32a7dc659234da60f664 100644 (file)
@@ -8,7 +8,7 @@ import {
 import { isFunction, EMPTY_OBJ } from '@vue/shared'
 import { RenderProxyHandlers } from './componentProxy'
 import { ComponentPropsOptions, PropValidator } from './componentProps'
-import { PROPS, SLOTS } from './patchFlags'
+import { PROPS, DYNAMIC_SLOTS, FULL_PROPS } from './patchFlags'
 
 export type Data = { [key: string]: any }
 
@@ -183,15 +183,18 @@ export function shouldUpdateComponent(
   prevVNode: VNode,
   nextVNode: VNode
 ): boolean {
-  const { props: prevProps } = prevVNode
-  const { props: nextProps, patchFlag } = nextVNode
+  const { props: prevProps, children: prevChildren } = prevVNode
+  const { props: nextProps, children: nextChildren, patchFlag } = nextVNode
   if (patchFlag !== null) {
-    if (patchFlag & SLOTS) {
+    if (patchFlag & DYNAMIC_SLOTS) {
       // slot content that references values that might have changed,
       // e.g. in a v-for
       return true
     }
-    if (patchFlag & PROPS) {
+    if (patchFlag & FULL_PROPS) {
+      // presence of this flag indicates props are always non-null
+      return hasPropsChanged(prevProps as Data, nextProps as Data)
+    } else if (patchFlag & PROPS) {
       const dynamicProps = nextVNode.dynamicProps as string[]
       for (let i = 0; i < dynamicProps.length; i++) {
         const key = dynamicProps[i]
@@ -201,7 +204,11 @@ export function shouldUpdateComponent(
       }
     }
   } else {
-    // TODO handle slots
+    // this path is only taken by manually written render functions
+    // so presence of any children leads to a forced update
+    if (prevChildren != null || nextChildren != null) {
+      return true
+    }
     if (prevProps === nextProps) {
       return false
     }
@@ -211,16 +218,21 @@ export function shouldUpdateComponent(
     if (nextProps === null) {
       return prevProps !== null
     }
-    const nextKeys = Object.keys(nextProps)
-    if (nextKeys.length !== Object.keys(prevProps).length) {
+    return hasPropsChanged(prevProps, nextProps)
+  }
+  return false
+}
+
+function hasPropsChanged(prevProps: Data, nextProps: Data): boolean {
+  const nextKeys = Object.keys(nextProps)
+  if (nextKeys.length !== Object.keys(prevProps).length) {
+    return true
+  }
+  for (let i = 0; i < nextKeys.length; i++) {
+    const key = nextKeys[i]
+    if (nextProps[key] !== prevProps[key]) {
       return true
     }
-    for (let i = 0; i < nextKeys.length; i++) {
-      const key = nextKeys[i]
-      if (nextProps[key] !== prevProps[key]) {
-        return true
-      }
-    }
   }
   return false
 }
index 77a8ea391b7229b2497bfe9c7658d9b22250311f..deda9abb15db298059933d59c3aa9fa13f63c71a 100644 (file)
@@ -22,7 +22,15 @@ import {
   EMPTY_OBJ,
   EMPTY_ARR
 } from '@vue/shared'
-import { TEXT, CLASS, STYLE, PROPS, KEYED, UNKEYED } from './patchFlags'
+import {
+  TEXT,
+  CLASS,
+  STYLE,
+  PROPS,
+  KEYED,
+  UNKEYED,
+  FULL_PROPS
+} from './patchFlags'
 import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
 import { effect, stop, ReactiveEffectOptions } from '@vue/observer'
 import { resolveProps } from './componentProps'
@@ -223,45 +231,50 @@ export function createRenderer(options: RendererOptions) {
       // in this path old node and new node are guaranteed to have the same shape
       // (i.e. at the exact same position in the source template)
 
-      // 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 (patchFlag & FULL_PROPS) {
+        // element props contain dynamic keys, full diff needed
+        patchProps(el, n2, oldProps, newProps)
+      } else {
+        // 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)
+          }
         }
-      }
 
-      // style
-      // this flag is matched when the element has dynamic style bindings
-      // TODO separate static and dynamic styles?
-      if (patchFlag & STYLE) {
-        hostPatchProp(el, 'style', newProps.style, oldProps.style, false)
-      }
+        // style
+        // this flag is matched when the element has dynamic style bindings
+        // TODO separate static and dynamic styles?
+        if (patchFlag & STYLE) {
+          hostPatchProp(el, 'style', newProps.style, oldProps.style, false)
+        }
 
-      // props
-      // This flag is matched when the element has dynamic prop/attr bindings
-      // other than class and style. The keys of dynamic prop/attrs are saved for
-      // faster iteration.
-      // Note dynamic keys like :[foo]="bar" will cause this optimization to
-      // bail out and go through a full diff because we need to unset the old key
-      if (patchFlag & PROPS) {
-        // if the flag is present then dynamicProps must be non-null
-        const propsToUpdate = n2.dynamicProps as string[]
-        for (let i = 0; i < propsToUpdate.length; i++) {
-          const key = propsToUpdate[i]
-          const prev = oldProps[key]
-          const next = newProps[key]
-          if (prev !== next) {
-            hostPatchProp(
-              el,
-              key,
-              next,
-              prev,
-              false,
-              n1.children as VNode[],
-              unmountChildren
-            )
+        // props
+        // This flag is matched when the element has dynamic prop/attr bindings
+        // other than class and style. The keys of dynamic prop/attrs are saved for
+        // faster iteration.
+        // Note dynamic keys like :[foo]="bar" will cause this optimization to
+        // bail out and go through a full diff because we need to unset the old key
+        if (patchFlag & PROPS) {
+          // if the flag is present then dynamicProps must be non-null
+          const propsToUpdate = n2.dynamicProps as string[]
+          for (let i = 0; i < propsToUpdate.length; i++) {
+            const key = propsToUpdate[i]
+            const prev = oldProps[key]
+            const next = newProps[key]
+            if (prev !== next) {
+              hostPatchProp(
+                el,
+                key,
+                next,
+                prev,
+                false,
+                n1.children as VNode[],
+                unmountChildren
+              )
+            }
           }
         }
       }
index ff6c52ce7da479255f07cea53c986b5ade59f6b5..53881ad9784e4301e46dddd944bfa2512d2dc572 100644 (file)
@@ -29,12 +29,18 @@ export const STYLE = 1 << 2
 // them faster (without having to worry about removed props)
 export const PROPS = 1 << 3
 
+// Indicates an element with props with dynamic keys. When keys change, a full
+// diff is always needed to remove the old key. This flag is mutually exclusive
+// with CLASS, STYLE and PROPS.
+export const FULL_PROPS = 1 << 4
+
 // Indicates a fragment or element with keyed or partially-keyed v-for children
-export const KEYED = 1 << 4
+export const KEYED = 1 << 5
 
 // Indicates a fragment or element that contains unkeyed v-for children
-export const UNKEYED = 1 << 5
+export const UNKEYED = 1 << 6
 
-// Indicates a component with dynamic slot (e.g. slot that references a v-for
-// iterated value). Components with this flag are always force updated.
-export const SLOTS = 1 << 6
+// Indicates a component with dynamic slots (e.g. slot that references a v-for
+// iterated value, or dynamic slot names).
+// Components with this flag are always force updated.
+export const DYNAMIC_SLOTS = 1 << 7