فهرست منبع

feat(runtime-core): watchSyncEffect

Evan You 4 سال پیش
والد
کامیت
d87d059ac1
3فایلهای تغییر یافته به همراه64 افزوده شده و 3 حذف شده
  1. 45 2
      packages/runtime-core/__tests__/apiWatch.spec.ts
  2. 13 0
      packages/runtime-core/src/apiWatch.ts
  3. 6 1
      packages/runtime-core/src/index.ts

+ 45 - 2
packages/runtime-core/__tests__/apiWatch.spec.ts

@@ -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()

+ 13 - 0
packages/runtime-core/src/apiWatch.ts

@@ -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 = {}
 

+ 6 - 1
packages/runtime-core/src/index.ts

@@ -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,