]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat: detailed info in renderTriggered + hint for skipping slot updates
authorEvan You <yyx990803@gmail.com>
Thu, 8 Nov 2018 23:20:07 +0000 (18:20 -0500)
committerEvan You <yyx990803@gmail.com>
Thu, 8 Nov 2018 23:20:07 +0000 (18:20 -0500)
packages/runtime-core/src/componentUtils.ts
packages/runtime-core/src/h.ts
packages/runtime-core/src/vdom.ts

index 0ad7caf99209614af606157d8c9bf0e6007f5865..7115205fbd3b2115848ede82ca273437c9344f2d 100644 (file)
@@ -204,9 +204,7 @@ export function shouldUpdateComponent(
   const { data: prevProps, childFlags: prevChildFlags } = prevVNode
   const { data: nextProps, childFlags: nextChildFlags } = nextVNode
   // If has different slots content, or has non-compiled slots,
-  // the child needs to be force updated. It's ok to call $forceUpdate
-  // again even if props update has already queued an update, as the
-  // scheduler will not queue the same update twice.
+  // the child needs to be force updated.
   if (
     prevChildFlags !== nextChildFlags ||
     (nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0
@@ -235,11 +233,49 @@ export function shouldUpdateComponent(
   return false
 }
 
+// DEV only
 export function getReasonForComponentUpdate(
   prevVNode: VNode,
   nextVNode: VNode
 ): any {
-  // TODO: extract more detailed information on why the component is updating
+  const reasons = []
+  const { childFlags: prevChildFlags } = prevVNode
+  const { childFlags: nextChildFlags } = nextVNode
+  if (
+    prevChildFlags !== nextChildFlags ||
+    (nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0
+  ) {
+    reasons.push({
+      type: `slots may have changed`,
+      tip: `use function slots + $stable: true to avoid slot-triggered child updates.`
+    })
+  }
+  const prevProps = prevVNode.data || EMPTY_OBJ
+  const nextProps = nextVNode.data || EMPTY_OBJ
+  for (const key in nextProps) {
+    if (nextProps[key] !== prevProps[key]) {
+      reasons.push({
+        type: 'prop changed',
+        key,
+        value: nextProps[key],
+        oldValue: prevProps[key]
+      })
+    }
+  }
+  for (const key in prevProps) {
+    if (!(key in nextProps)) {
+      reasons.push({
+        type: 'prop changed',
+        key,
+        value: undefined,
+        oldValue: prevProps[key]
+      })
+    }
+  }
+  return {
+    type: 'triggered by parent',
+    reasons
+  }
 }
 
 export function createComponentClassFromOptions(
index b9b43b2b66676320f2c17372a9a6cc6ad38fa730..627ea0fa70a2bb9bcaf503d3a1f441eaaff3b34a 100644 (file)
@@ -22,7 +22,8 @@ export const Portal = Symbol()
 type RawChildType = VNode | string | number | boolean | null | undefined
 
 export type RawSlots = {
-  [name: string]: () => RawChildrenType
+  $stable?: boolean
+  [name: string]: RawChildType | (() => RawChildrenType)
 }
 
 export type RawChildrenType = RawChildType | RawChildType[]
index 451a67adda4c59bae2bcde40f52c97871b6b28b1..be222b6700dc8ef6d529ba627971b99fc8f090bc 100644 (file)
@@ -10,6 +10,7 @@ import { RawChildrenType, RawSlots } from './h'
 import { FunctionalHandle } from './createRenderer'
 
 const handlersRE = /^on|^vnode/
+const STABLE_SLOTS_HINT = '$stable'
 
 // Vue core is platform agnostic, so we are not using Element for "DOM" nodes.
 export interface RenderNode {
@@ -226,6 +227,10 @@ export function createComponentVNode(
       } else if (isObject(children) && !(children as any)._isVNode) {
         // slot object as children
         slots = children
+        // special manual optimization hint for raw render fn users
+        if (slots[STABLE_SLOTS_HINT]) {
+          childFlags = ChildrenFlags.STABLE_SLOTS
+        }
       } else {
         slots = { default: () => children }
       }
@@ -418,6 +423,9 @@ function normalizeSlots(slots: { [name: string]: any }): Slots {
   }
   const normalized = { _normalized: true } as any
   for (const name in slots) {
+    if (name === STABLE_SLOTS_HINT) {
+      continue
+    }
     normalized[name] = (...args: any[]) => normalizeSlot(slots[name](...args))
   }
   return normalized