]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(runtime-core): add watchEffect API
authorEvan You <yyx990803@gmail.com>
Sat, 22 Feb 2020 07:19:10 +0000 (08:19 +0100)
committerEvan You <yyx990803@gmail.com>
Sat, 22 Feb 2020 07:19:10 +0000 (08:19 +0100)
BREAKING CHANGE: replae `watch(fn, options?)` with `watchEffect`

    The `watch(fn, options?)` signature has been replaced by the new
    `watchEffect` API, which has the same usage and behavior. `watch`
    now only supports the `watch(source, cb, options?)` signautre.

packages/runtime-core/__tests__/apiSetupContext.spec.ts
packages/runtime-core/__tests__/apiWatch.spec.ts
packages/runtime-core/__tests__/components/Suspense.spec.ts
packages/runtime-core/__tests__/errorHandling.spec.ts
packages/runtime-core/src/apiWatch.ts
packages/runtime-core/src/index.ts
packages/vue/examples/composition/commits.html
packages/vue/examples/composition/todomvc.html

index bc3c2065034a5ef259c5bed09ead13c5c95b9466..8f7ab078bbeeacef3543fdf2c1cc68bb119fbedd 100644 (file)
@@ -6,7 +6,7 @@ import {
   render,
   serializeInner,
   nextTick,
-  watch,
+  watchEffect,
   defineComponent,
   triggerEvent,
   TestElement
@@ -55,7 +55,7 @@ describe('api: setup context', () => {
 
     const Child = defineComponent({
       setup(props: { count: number }) {
-        watch(() => {
+        watchEffect(() => {
           dummy = props.count
         })
         return () => h('div', props.count)
@@ -88,7 +88,7 @@ describe('api: setup context', () => {
       },
 
       setup(props) {
-        watch(() => {
+        watchEffect(() => {
           dummy = props.count
         })
         return () => h('div', props.count)
index ef05452fd33bef47abfd44d638fd2760093716fe..796b2cbff50d4d4d516c9608777b85bd0fac426a 100644 (file)
@@ -1,4 +1,12 @@
-import { watch, reactive, computed, nextTick, ref, h } from '../src/index'
+import {
+  watch,
+  watchEffect,
+  reactive,
+  computed,
+  nextTick,
+  ref,
+  h
+} from '../src/index'
 import { render, nodeOps, serializeInner } from '@vue/runtime-test'
 import {
   ITERATE_KEY,
@@ -13,10 +21,10 @@ import { mockWarn } from '@vue/shared'
 describe('api: watch', () => {
   mockWarn()
 
-  it('watch(effect)', async () => {
+  it('effect', async () => {
     const state = reactive({ count: 0 })
     let dummy
-    watch(() => {
+    watchEffect(() => {
       dummy = state.count
     })
     expect(dummy).toBe(0)
@@ -117,10 +125,10 @@ describe('api: watch', () => {
     expect(dummy).toMatchObject([[2, true], [1, false]])
   })
 
-  it('stopping the watcher', async () => {
+  it('stopping the watcher (effect)', async () => {
     const state = reactive({ count: 0 })
     let dummy
-    const stop = watch(() => {
+    const stop = watchEffect(() => {
       dummy = state.count
     })
     expect(dummy).toBe(0)
@@ -132,11 +140,32 @@ describe('api: watch', () => {
     expect(dummy).toBe(0)
   })
 
+  it('stopping the watcher (with source)', async () => {
+    const state = reactive({ count: 0 })
+    let dummy
+    const stop = watch(
+      () => state.count,
+      count => {
+        dummy = count
+      }
+    )
+
+    state.count++
+    await nextTick()
+    expect(dummy).toBe(1)
+
+    stop()
+    state.count++
+    await nextTick()
+    // should not update
+    expect(dummy).toBe(1)
+  })
+
   it('cleanup registration (effect)', async () => {
     const state = reactive({ count: 0 })
     const cleanup = jest.fn()
     let dummy
-    const stop = watch(onCleanup => {
+    const stop = watchEffect(onCleanup => {
       onCleanup(cleanup)
       dummy = state.count
     })
@@ -187,7 +216,7 @@ describe('api: watch', () => {
 
     const Comp = {
       setup() {
-        watch(() => {
+        watchEffect(() => {
           assertion(count.value)
         })
         return () => count.value
@@ -221,7 +250,7 @@ describe('api: watch', () => {
 
     const Comp = {
       setup() {
-        watch(
+        watchEffect(
           () => {
             assertion(count.value, count2.value)
           },
@@ -263,7 +292,7 @@ describe('api: watch', () => {
 
     const Comp = {
       setup() {
-        watch(
+        watchEffect(
           () => {
             assertion(count.value)
           },
@@ -363,14 +392,14 @@ describe('api: watch', () => {
     expect(spy).toHaveBeenCalledTimes(3)
   })
 
-  it('warn immediate option when using effect signature', async () => {
+  it('warn immediate option when using effect', async () => {
     const count = ref(0)
     let dummy
-    // @ts-ignore
-    watch(
+    watchEffect(
       () => {
         dummy = count.value
       },
+      // @ts-ignore
       { immediate: false }
     )
     expect(dummy).toBe(0)
@@ -388,7 +417,7 @@ describe('api: watch', () => {
       events.push(e)
     })
     const obj = reactive({ foo: 1, bar: 2 })
-    watch(
+    watchEffect(
       () => {
         dummy = [obj.foo, 'bar' in obj, Object.keys(obj)]
       },
@@ -423,7 +452,7 @@ describe('api: watch', () => {
       events.push(e)
     })
     const obj = reactive({ foo: 1 })
-    watch(
+    watchEffect(
       () => {
         dummy = obj.foo
       },
index a530532f0771d3156e2dc22275520b8f8f6cd497..6a115e39c250510312e8ce8a288f17be84811d6e 100644 (file)
@@ -9,6 +9,7 @@ import {
   nextTick,
   onMounted,
   watch,
+  watchEffect,
   onUnmounted,
   onErrorCaptured
 } from '@vue/runtime-test'
@@ -163,7 +164,7 @@ describe('Suspense', () => {
         // extra tick needed for Node 12+
         deps.push(p.then(() => Promise.resolve()))
 
-        watch(() => {
+        watchEffect(() => {
           calls.push('immediate effect')
         })
 
@@ -265,7 +266,7 @@ describe('Suspense', () => {
         const p = new Promise(r => setTimeout(r, 1))
         deps.push(p)
 
-        watch(() => {
+        watchEffect(() => {
           calls.push('immediate effect')
         })
 
index 6396c6b35a4a82bea12d714dfca5fb5ed653d2a3..f6907949b1f67cbb28462780fe43d71395c425b2 100644 (file)
@@ -7,7 +7,8 @@ import {
   watch,
   ref,
   nextTick,
-  defineComponent
+  defineComponent,
+  watchEffect
 } from '@vue/runtime-test'
 import { setErrorRecovery } from '../src/errorHandling'
 import { mockWarn } from '@vue/shared'
@@ -241,7 +242,7 @@ describe('error handling', () => {
     expect(fn).toHaveBeenCalledWith(err, 'ref function')
   })
 
-  test('in watch (effect)', () => {
+  test('in effect', () => {
     const err = new Error('foo')
     const fn = jest.fn()
 
@@ -257,7 +258,7 @@ describe('error handling', () => {
 
     const Child = {
       setup() {
-        watch(() => {
+        watchEffect(() => {
           throw err
         })
         return () => null
@@ -268,7 +269,7 @@ describe('error handling', () => {
     expect(fn).toHaveBeenCalledWith(err, 'watcher callback')
   })
 
-  test('in watch (getter)', () => {
+  test('in watch getter', () => {
     const err = new Error('foo')
     const fn = jest.fn()
 
@@ -298,7 +299,7 @@ describe('error handling', () => {
     expect(fn).toHaveBeenCalledWith(err, 'watcher getter')
   })
 
-  test('in watch (callback)', async () => {
+  test('in watch callback', async () => {
     const err = new Error('foo')
     const fn = jest.fn()
 
@@ -332,7 +333,7 @@ describe('error handling', () => {
     expect(fn).toHaveBeenCalledWith(err, 'watcher callback')
   })
 
-  test('in watch cleanup', async () => {
+  test('in effect cleanup', async () => {
     const err = new Error('foo')
     const count = ref(0)
     const fn = jest.fn()
@@ -349,7 +350,7 @@ describe('error handling', () => {
 
     const Child = {
       setup() {
-        watch(onCleanup => {
+        watchEffect(onCleanup => {
           count.value
           onCleanup(() => {
             throw err
index da3df3033f94500a3d5d9803f6dedd642f65d340..1a2ecb5ec2fbb3379abcbeef252508066ff3689d 100644 (file)
@@ -71,6 +71,14 @@ export type StopHandle = () => void
 
 const invoke = (fn: Function) => fn()
 
+// Simple effect.
+export function watchEffect(
+  effect: WatchEffect,
+  options?: BaseWatchOptions
+): StopHandle {
+  return doWatch(effect, null, options)
+}
+
 // initial value for watchers to trigger on undefined initial values
 const INITIAL_WATCHER_VALUE = {}
 
@@ -110,6 +118,13 @@ export function watch<T = any>(
     // watch(source, cb)
     return doWatch(effectOrSource, cbOrOptions, options)
   } else {
+    // TODO remove this in the next release
+    __DEV__ &&
+      warn(
+        `\`watch(fn, options?)\` signature has been moved to a separate API. ` +
+          `Use \`watchEffect(fn, options?)\` instead. \`watch\` will only ` +
+          `support \`watch(source, cb, options?) signature in the next release.`
+      )
     // watch(effect)
     return doWatch(effectOrSource, null, cbOrOptions)
   }
index 8629d07f418d4500106e8120ba1c4a7d3d628af0..634d53b424b89ca5cf25389cfb6f11e2266b966d 100644 (file)
@@ -17,7 +17,7 @@ export {
   markNonReactive
 } from '@vue/reactivity'
 export { computed } from './apiComputed'
-export { watch } from './apiWatch'
+export { watch, watchEffect } from './apiWatch'
 export {
   onBeforeMount,
   onMounted,
index 270662fe3865c4a74d4f4fdc42e2d1a035fba4e8..711cbc67039aa6d687d90e57b77c85845278ed1f 100644 (file)
@@ -22,7 +22,7 @@
 </div>
 
 <script>
-const { createApp, ref, watch } = Vue
+const { createApp, ref, watchEffect } = Vue
 const API_URL = `https://api.github.com/repos/vuejs/vue-next/commits?per_page=3&sha=`
 
 const truncate = v => {
@@ -37,7 +37,7 @@ createApp({
     const currentBranch = ref('master')
     const commits = ref(null)
 
-    watch(() => {
+    watchEffect(() => {
       fetch(`${API_URL}${currentBranch.value}`)
         .then(res => res.json())
         .then(data => {
index 791654e0581a0f99c1c050947a5f37a612f09046..37bfeb86cb022cec0ed629e33c971f01b1026938 100644 (file)
@@ -53,7 +53,7 @@
 </div>
 
 <script>
-const { createApp, reactive, computed, watch, onMounted, onUnmounted } = Vue
+const { createApp, reactive, computed, watchEffect, onMounted, onUnmounted } = Vue
 
 const STORAGE_KEY = 'todos-vuejs-3.x'
 const todoStorage = {
@@ -119,7 +119,7 @@ createApp({
       })
     })
 
-    watch(() => {
+    watchEffect(() => {
       todoStorage.save(state.todos)
     })