]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(runtime-core): watchSyncEffect
authorEvan You <yyx990803@gmail.com>
Tue, 20 Jul 2021 20:49:54 +0000 (16:49 -0400)
committerEvan You <yyx990803@gmail.com>
Tue, 20 Jul 2021 20:49:54 +0000 (16:49 -0400)
packages/runtime-core/__tests__/apiWatch.spec.ts
packages/runtime-core/src/apiWatch.ts
packages/runtime-core/src/index.ts

index 6ac335eaa58a265fa4dd4bf927ea76861101f4c8..e25aca26776a3a47a1417e2eb086214b4478278c 100644 (file)
@@ -16,7 +16,9 @@ import {
   serializeInner,
   TestElement,
   h,
-  createApp
+  createApp,
+  watchPostEffect,
+  watchSyncEffect
 } from '@vue/runtime-test'
 import {
   ITERATE_KEY,
@@ -28,7 +30,6 @@ import {
   Ref,
   effectScope
 } from '@vue/reactivity'
-import { watchPostEffect } from '../src/apiWatch'
 
 // reference: https://vue-composition-api-rfc.netlify.com/api.html#watch
 
@@ -444,6 +445,48 @@ describe('api: watch', () => {
     expect(result2).toBe(true)
   })
 
+  it('watchSyncEffect', async () => {
+    const count = ref(0)
+    const count2 = ref(0)
+
+    let callCount = 0
+    let result1
+    let result2
+    const assertion = jest.fn(count => {
+      callCount++
+      // on mount, the watcher callback should be called before DOM render
+      // on update, should be called before the count is updated
+      const expectedDOM = callCount === 1 ? `` : `${count - 1}`
+      result1 = serializeInner(root) === expectedDOM
+
+      // in a sync callback, state mutation on the next line should not have
+      // executed yet on the 2nd call, but will be on the 3rd call.
+      const expectedState = callCount < 3 ? 0 : 1
+      result2 = count2.value === expectedState
+    })
+
+    const Comp = {
+      setup() {
+        watchSyncEffect(() => {
+          assertion(count.value)
+        })
+        return () => count.value
+      }
+    }
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+    expect(assertion).toHaveBeenCalledTimes(1)
+    expect(result1).toBe(true)
+    expect(result2).toBe(true)
+
+    count.value++
+    count2.value++
+    await nextTick()
+    expect(assertion).toHaveBeenCalledTimes(3)
+    expect(result1).toBe(true)
+    expect(result2).toBe(true)
+  })
+
   it('should not fire on component unmount w/ flush: post', async () => {
     const toggle = ref(true)
     const cb = jest.fn()
index f89694df8048ca5ab7aeca32d7e95634b44fb0f5..4a41485629ca63281ab87d4d00240d7dc7400979 100644 (file)
@@ -96,6 +96,19 @@ export function watchPostEffect(
   )
 }
 
+export function watchSyncEffect(
+  effect: WatchEffect,
+  options?: DebuggerOptions
+) {
+  return doWatch(
+    effect,
+    null,
+    (__DEV__
+      ? Object.assign(options || {}, { flush: 'sync' })
+      : { flush: 'sync' }) as WatchOptionsBase
+  )
+}
+
 // initial value for watchers to trigger on undefined initial values
 const INITIAL_WATCHER_VALUE = {}
 
index 4ce2a82c42b9a7d0f6e58c6347d79d9b64abe186..26e70dd9141076516156eff89a86a94501e3ba9f 100644 (file)
@@ -34,7 +34,12 @@ export {
   getCurrentScope,
   onScopeDispose
 } from '@vue/reactivity'
-export { watch, watchEffect, watchPostEffect } from './apiWatch'
+export {
+  watch,
+  watchEffect,
+  watchPostEffect,
+  watchSyncEffect
+} from './apiWatch'
 export {
   onBeforeMount,
   onMounted,