]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat: renderTriggered for forced updates
authorEvan You <yyx990803@gmail.com>
Thu, 8 Nov 2018 19:09:52 +0000 (14:09 -0500)
committerEvan You <yyx990803@gmail.com>
Thu, 8 Nov 2018 19:09:52 +0000 (14:09 -0500)
packages/observer/src/index.ts
packages/runtime-core/src/componentUtils.ts
packages/runtime-core/src/createRenderer.ts
packages/runtime-core/src/errorHandling.ts

index 836731ed43f0cc926555c96e3fa93e744bb784ea..005b82d3b94b5c6b98827d0d0cfa1afe82841cb0 100644 (file)
@@ -24,7 +24,7 @@ import {
   DebuggerEvent
 } from './autorun'
 
-export { Autorun, DebuggerEvent }
+export { Autorun, AutorunOptions, DebuggerEvent }
 export { OperationTypes } from './operations'
 export { computed, ComputedGetter } from './computed'
 export { lock, unlock } from './lock'
index c7dda26ba12bfc91d808c2512c111de8f64b9649..0ad7caf99209614af606157d8c9bf0e6007f5865 100644 (file)
@@ -21,7 +21,7 @@ import { createRenderProxy } from './componentProxy'
 import {
   handleError,
   ErrorTypes,
-  callLifecycleHookWithHandle
+  callLifecycleHookWithHandler
 } from './errorHandling'
 import { warn } from './warning'
 import { setCurrentInstance, unsetCurrentInstance } from './experimental/hooks'
@@ -56,7 +56,7 @@ export function createComponentInstance<T extends Component>(
   instance.$slots = currentVNode.slots || EMPTY_OBJ
 
   if (created) {
-    callLifecycleHookWithHandle(created, $proxy, ErrorTypes.CREATED)
+    callLifecycleHookWithHandler(created, $proxy, ErrorTypes.CREATED)
   }
 
   currentVNode = currentContextVNode = null
@@ -100,7 +100,7 @@ export function initializeComponentInstance(instance: ComponentInstance) {
   // beforeCreate hook is called right in the constructor
   const { beforeCreate, props } = instance.$options
   if (beforeCreate) {
-    callLifecycleHookWithHandle(beforeCreate, proxy, ErrorTypes.BEFORE_CREATE)
+    callLifecycleHookWithHandler(beforeCreate, proxy, ErrorTypes.BEFORE_CREATE)
   }
   initializeProps(instance, props, (currentVNode as VNode).data)
 }
@@ -222,18 +222,24 @@ export function shouldUpdateComponent(
   if (nextProps === null) {
     return prevProps !== null
   }
-  let shouldUpdate = true
   const nextKeys = Object.keys(nextProps)
-  if (nextKeys.length === Object.keys(prevProps).length) {
-    shouldUpdate = false
-    for (let i = 0; i < nextKeys.length; i++) {
-      const key = nextKeys[i]
-      if (nextProps[key] !== prevProps[key]) {
-        shouldUpdate = true
-      }
+  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
     }
   }
-  return shouldUpdate
+  return false
+}
+
+export function getReasonForComponentUpdate(
+  prevVNode: VNode,
+  nextVNode: VNode
+): any {
+  // TODO: extract more detailed information on why the component is updating
 }
 
 export function createComponentClassFromOptions(
index 41a6845055a2ed8d51a9091015870419385e0d76..afa26caa0c4049c63570cd7d8bfb8a246ec282f8 100644 (file)
@@ -1,4 +1,10 @@
-import { autorun, stop, Autorun, immutable } from '@vue/observer'
+import {
+  autorun,
+  stop,
+  Autorun,
+  immutable,
+  AutorunOptions
+} from '@vue/observer'
 import { queueJob, handleSchedulerError, nextTick } from '@vue/scheduler'
 import { VNodeFlags, ChildrenFlags } from './flags'
 import { EMPTY_OBJ, reservedPropRE, isString } from '@vue/shared'
@@ -15,7 +21,8 @@ import {
   renderFunctionalRoot,
   createComponentInstance,
   teardownComponentInstance,
-  shouldUpdateComponent
+  shouldUpdateComponent,
+  getReasonForComponentUpdate
 } from './componentUtils'
 import { KeepAliveSymbol } from './optional/keepAlive'
 import { pushWarningContext, popWarningContext, warn } from './warning'
@@ -23,7 +30,7 @@ import { resolveProps } from './componentProps'
 import {
   handleError,
   ErrorTypes,
-  callLifecycleHookWithHandle
+  callLifecycleHookWithHandler
 } from './errorHandling'
 
 export interface NodeOps {
@@ -561,6 +568,14 @@ export function createRenderer(options: RendererOptions) {
     instance.$parentVNode = nextVNode as MountedVNode
 
     if (shouldUpdateComponent(prevVNode, nextVNode)) {
+      if (__DEV__ && instance.$options.renderTriggered) {
+        callLifecycleHookWithHandler(
+          instance.$options.renderTriggered,
+          instance.$proxy,
+          ErrorTypes.RENDER_TRIGGERED,
+          getReasonForComponentUpdate(prevVNode, nextVNode)
+        )
+      }
       instance.$forceUpdate()
     } else if (instance.$vnode.flags & VNodeFlags.COMPONENT) {
       instance.$vnode.contextVNode = nextVNode
@@ -1194,58 +1209,72 @@ export function createRenderer(options: RendererOptions) {
     } = instance
 
     if (beforeMount) {
-      callLifecycleHookWithHandle(beforeMount, $proxy, ErrorTypes.BEFORE_MOUNT)
+      callLifecycleHookWithHandler(beforeMount, $proxy, ErrorTypes.BEFORE_MOUNT)
     }
 
     const queueUpdate = (instance.$forceUpdate = () => {
       queueJob(instance._updateHandle, flushHooks)
     })
 
-    instance._updateHandle = autorun(
-      () => {
-        if (instance._unmounted) {
-          return
+    const autorunOptions: AutorunOptions = {
+      scheduler: queueUpdate
+    }
+
+    if (__DEV__) {
+      if (renderTracked) {
+        autorunOptions.onTrack = event => {
+          callLifecycleHookWithHandler(
+            renderTracked,
+            $proxy,
+            ErrorTypes.RENDER_TRACKED,
+            event
+          )
         }
-        if (instance._mounted) {
-          updateComponentInstance(instance, isSVG)
-        } else {
-          // this will be executed synchronously right here
-          instance.$vnode = renderInstanceRoot(instance) as MountedVNode
-
-          queuePostCommitHook(() => {
-            vnode.el = instance.$vnode.el
-            if (__COMPAT__) {
-              // expose __vue__ for devtools
-              ;(vnode.el as any).__vue__ = instance
-            }
-            if (vnode.ref) {
-              vnode.ref($proxy)
-            }
-            // retrieve mounted value after initial render so that we get
-            // to inject effects in hooks
-            const { mounted } = instance.$options
-            if (mounted) {
-              callLifecycleHookWithHandle(mounted, $proxy, ErrorTypes.MOUNTED)
-            }
-          })
-
-          mount(
-            instance.$vnode,
-            container,
-            vnode as MountedVNode,
-            isSVG,
-            endNode
+      }
+      if (renderTriggered) {
+        autorunOptions.onTrigger = event => {
+          callLifecycleHookWithHandler(
+            renderTriggered,
+            $proxy,
+            ErrorTypes.RENDER_TRIGGERED,
+            event
           )
-
-          instance._mounted = true
         }
-      },
-      {
-        scheduler: queueUpdate,
-        onTrack: renderTracked,
-        onTrigger: renderTriggered
       }
-    )
+    }
+
+    instance._updateHandle = autorun(() => {
+      if (instance._unmounted) {
+        return
+      }
+      if (instance._mounted) {
+        updateComponentInstance(instance, isSVG)
+      } else {
+        // this will be executed synchronously right here
+        instance.$vnode = renderInstanceRoot(instance) as MountedVNode
+
+        queuePostCommitHook(() => {
+          vnode.el = instance.$vnode.el
+          if (__COMPAT__) {
+            // expose __vue__ for devtools
+            ;(vnode.el as any).__vue__ = instance
+          }
+          if (vnode.ref) {
+            vnode.ref($proxy)
+          }
+          // retrieve mounted value after initial render so that we get
+          // to inject effects in hooks
+          const { mounted } = instance.$options
+          if (mounted) {
+            callLifecycleHookWithHandler(mounted, $proxy, ErrorTypes.MOUNTED)
+          }
+        })
+
+        mount(instance.$vnode, container, vnode as MountedVNode, isSVG, endNode)
+
+        instance._mounted = true
+      }
+    }, autorunOptions)
 
     if (__DEV__) {
       popWarningContext()
@@ -1267,7 +1296,7 @@ export function createRenderer(options: RendererOptions) {
       $options: { beforeUpdate }
     } = instance
     if (beforeUpdate) {
-      callLifecycleHookWithHandle(
+      callLifecycleHookWithHandler(
         beforeUpdate,
         $proxy,
         ErrorTypes.BEFORE_UPDATE,
@@ -1297,7 +1326,7 @@ export function createRenderer(options: RendererOptions) {
       }
       const { updated } = instance.$options
       if (updated) {
-        callLifecycleHookWithHandle(
+        callLifecycleHookWithHandler(
           updated,
           $proxy,
           ErrorTypes.UPDATED,
@@ -1334,7 +1363,7 @@ export function createRenderer(options: RendererOptions) {
       $options: { beforeUnmount, unmounted }
     } = instance
     if (beforeUnmount) {
-      callLifecycleHookWithHandle(
+      callLifecycleHookWithHandler(
         beforeUnmount,
         $proxy,
         ErrorTypes.BEFORE_UNMOUNT
@@ -1347,7 +1376,7 @@ export function createRenderer(options: RendererOptions) {
     teardownComponentInstance(instance)
     instance._unmounted = true
     if (unmounted) {
-      callLifecycleHookWithHandle(unmounted, $proxy, ErrorTypes.UNMOUNTED)
+      callLifecycleHookWithHandler(unmounted, $proxy, ErrorTypes.UNMOUNTED)
     }
   }
 
@@ -1391,7 +1420,7 @@ export function createRenderer(options: RendererOptions) {
         callActivatedHook($children[i], false)
       }
       if (activated) {
-        callLifecycleHookWithHandle(activated, $proxy, ErrorTypes.ACTIVATED)
+        callLifecycleHookWithHandler(activated, $proxy, ErrorTypes.ACTIVATED)
       }
     }
   }
@@ -1416,7 +1445,11 @@ export function createRenderer(options: RendererOptions) {
         callDeactivateHook($children[i], false)
       }
       if (deactivated) {
-        callLifecycleHookWithHandle(deactivated, $proxy, ErrorTypes.DEACTIVATED)
+        callLifecycleHookWithHandler(
+          deactivated,
+          $proxy,
+          ErrorTypes.DEACTIVATED
+        )
       }
     }
   }
index 1205d5fafcb65de23e815a77ae867d72810cb36a..301fa66bd3192f413f138218c05cb57988276fe1 100644 (file)
@@ -16,6 +16,8 @@ export const enum ErrorTypes {
   DEACTIVATED,
   ERROR_CAPTURED,
   RENDER,
+  RENDER_TRACKED,
+  RENDER_TRIGGERED,
   WATCH_CALLBACK,
   NATIVE_EVENT_HANDLER,
   COMPONENT_EVENT_HANDLER,
@@ -35,6 +37,8 @@ const ErrorTypeStrings: Record<number, string> = {
   [ErrorTypes.DEACTIVATED]: 'in deactivated lifecycle hook',
   [ErrorTypes.ERROR_CAPTURED]: 'in errorCaptured lifecycle hook',
   [ErrorTypes.RENDER]: 'in render function',
+  [ErrorTypes.RENDER_TRACKED]: 'in renderTracked debug hook',
+  [ErrorTypes.RENDER_TRIGGERED]: 'in renderTriggered debug hook',
   [ErrorTypes.WATCH_CALLBACK]: 'in watcher callback',
   [ErrorTypes.NATIVE_EVENT_HANDLER]: 'in native event handler',
   [ErrorTypes.COMPONENT_EVENT_HANDLER]: 'in component event handler',
@@ -42,7 +46,7 @@ const ErrorTypeStrings: Record<number, string> = {
     'when flushing updates. This may be a Vue internals bug.'
 }
 
-export function callLifecycleHookWithHandle(
+export function callLifecycleHookWithHandler(
   hook: Function,
   instanceProxy: ComponentInstance,
   type: ErrorTypes,