]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(reactivity): only clear notified flags for computed in first batch iteration
authorEvan You <evan@vuejs.org>
Fri, 27 Sep 2024 03:22:36 +0000 (11:22 +0800)
committerEvan You <evan@vuejs.org>
Fri, 27 Sep 2024 03:25:58 +0000 (11:25 +0800)
close #12045

packages/reactivity/src/computed.ts
packages/reactivity/src/effect.ts
packages/runtime-core/__tests__/apiWatch.spec.ts

index b16b011c5b75ba7e99ecd84473548b0b505f8455..628cf46367b3e22715a864410ad23c74ddf75120 100644 (file)
@@ -84,9 +84,13 @@ export class ComputedRefImpl<T = any> implements Subscriber {
    * @internal
    */
   isSSR: boolean
+  /**
+   * @internal
+   */
+  next?: Subscriber = undefined
+
   // for backwards compat
   effect: this = this
-
   // dev only
   onTrack?: (event: DebuggerEvent) => void
   // dev only
index 15693ee39b45a11c4a1625b0ec3564d26839baff..b32355081e77a10c99b5e256268dd8739a9afc3a 100644 (file)
@@ -261,13 +261,20 @@ export function endBatch(): void {
   while (batchedSub) {
     let e: Subscriber | undefined = batchedSub
     let next: Subscriber | undefined
+    // 1st pass: clear notified flags for computed upfront
+    // we use the ACTIVE flag as a discriminator between computed and effect,
+    // since NOTIFIED is useless for an inactive effect anyway.
     while (e) {
-      e.flags &= ~EffectFlags.NOTIFIED
+      if (!(e.flags & EffectFlags.ACTIVE)) {
+        e.flags &= ~EffectFlags.NOTIFIED
+      }
       e = e.next
     }
     e = batchedSub
     batchedSub = undefined
+    // 2nd pass: run effects
     while (e) {
+      e.flags &= ~EffectFlags.NOTIFIED
       if (e.flags & EffectFlags.ACTIVE) {
         try {
           // ACTIVE flag is effect-only
index 082d585b85278dcfa005dc7ff8bdb2807001b5b1..10a4fe659e09e39f84b2c73ec582eb0ef92840bf 100644 (file)
@@ -1930,7 +1930,7 @@ describe('api: watch', () => {
     warn.mockRestore()
   })
 
-  it('should be executed correctly', () => {
+  test('should be executed correctly', () => {
     const v = ref(1)
     let foo = ''
 
@@ -1957,4 +1957,30 @@ describe('api: watch', () => {
     v.value++
     expect(foo).toBe('12')
   })
+
+  // 12045
+  test('sync watcher should not break pre watchers', async () => {
+    const count1 = ref(0)
+    const count2 = ref(0)
+
+    watch(
+      count1,
+      () => {
+        count2.value++
+      },
+      { flush: 'sync' },
+    )
+
+    const spy1 = vi.fn()
+    watch([count1, count2], spy1)
+
+    const spy2 = vi.fn()
+    watch(count1, spy2)
+
+    count1.value++
+
+    await nextTick()
+    expect(spy1).toHaveBeenCalled()
+    expect(spy2).toHaveBeenCalled()
+  })
 })