]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): disallow recurse in vnode/directive beforeUpdate hooks
authorEvan You <yyx990803@gmail.com>
Fri, 10 Dec 2021 09:17:08 +0000 (17:17 +0800)
committerEvan You <yyx990803@gmail.com>
Fri, 10 Dec 2021 09:17:15 +0000 (17:17 +0800)
packages/runtime-core/src/component.ts
packages/runtime-core/src/renderer.ts

index cda15d8bd0741a27fb2ce62a5bd4d05e6775b0b1..eb80e3f9f7f18e0db17e1b0e81b290dc554a59b5 100644 (file)
@@ -7,7 +7,8 @@ import {
   EffectScope,
   markRaw,
   track,
-  TrackOpTypes
+  TrackOpTypes,
+  ReactiveEffect
 } from '@vue/reactivity'
 import {
   ComponentPublicInstance,
@@ -224,6 +225,10 @@ export interface ComponentInternalInstance {
    * Root vnode of this component's own vdom tree
    */
   subTree: VNode
+  /**
+   * Render effect instance
+   */
+  effect: ReactiveEffect
   /**
    * Bound effect runner to be passed to schedulers
    */
@@ -460,6 +465,7 @@ export function createComponentInstance(
     root: null!, // to be immediately set
     next: null,
     subTree: null!, // will be set synchronously right after creation
+    effect: null!,
     update: null!, // will be set synchronously right after creation
     scope: new EffectScope(true /* detached */),
     render: null,
index 7cf8b49521fe1d83642e140e43f955026887b6e1..3892eebee8d3a243bdee289a60a3b35c39aeee4f 100644 (file)
@@ -826,12 +826,15 @@ function baseCreateRenderer(
     const newProps = n2.props || EMPTY_OBJ
     let vnodeHook: VNodeHook | undefined | null
 
+    // disable recurse in beforeUpdate hooks
+    parentComponent && toggleRecurse(parentComponent, false)
     if ((vnodeHook = newProps.onVnodeBeforeUpdate)) {
       invokeVNodeHook(vnodeHook, parentComponent, n2, n1)
     }
     if (dirs) {
       invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate')
     }
+    parentComponent && toggleRecurse(parentComponent, true)
 
     if (__DEV__ && isHmrUpdating) {
       // HMR updated, force full diff
@@ -1318,7 +1321,7 @@ function baseCreateRenderer(
         const { bm, m, parent } = instance
         const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)
 
-        effect.allowRecurse = false
+        toggleRecurse(instance, false)
         // beforeMount hook
         if (bm) {
           invokeArrayFns(bm)
@@ -1336,7 +1339,7 @@ function baseCreateRenderer(
         ) {
           instance.emit('hook:beforeMount')
         }
-        effect.allowRecurse = true
+        toggleRecurse(instance, true)
 
         if (el && hydrateNode) {
           // vnode has adopted host node - perform hydration instead of mount.
@@ -1459,8 +1462,7 @@ function baseCreateRenderer(
         }
 
         // Disallow component effect recursion during pre-lifecycle hooks.
-        effect.allowRecurse = false
-
+        toggleRecurse(instance, false)
         if (next) {
           next.el = vnode.el
           updateComponentPreRender(instance, next, optimized)
@@ -1482,8 +1484,7 @@ function baseCreateRenderer(
         ) {
           instance.emit('hook:beforeUpdate')
         }
-
-        effect.allowRecurse = true
+        toggleRecurse(instance, true)
 
         // render
         if (__DEV__) {
@@ -1552,17 +1553,17 @@ function baseCreateRenderer(
     }
 
     // create reactive effect for rendering
-    const effect = new ReactiveEffect(
+    const effect = (instance.effect = new ReactiveEffect(
       componentUpdateFn,
       () => queueJob(instance.update),
       instance.scope // track it in component's effect scope
-    )
+    ))
 
     const update = (instance.update = effect.run.bind(effect) as SchedulerJob)
     update.id = instance.uid
     // allowRecurse
     // #1801, #2043 component render effects should allow recursive updates
-    effect.allowRecurse = update.allowRecurse = true
+    toggleRecurse(instance, true)
 
     if (__DEV__) {
       effect.onTrack = instance.rtc
@@ -2455,6 +2456,13 @@ export function invokeVNodeHook(
   ])
 }
 
+function toggleRecurse(
+  { effect, update }: ComponentInternalInstance,
+  allowed: boolean
+) {
+  effect.allowRecurse = update.allowRecurse = allowed
+}
+
 /**
  * #1156
  * When a component is HMR-enabled, we need to make sure that all static nodes