diff --git a/packages/global.d.ts b/packages/global.d.ts index 79b55171384..57dd00a9fab 100644 --- a/packages/global.d.ts +++ b/packages/global.d.ts @@ -9,6 +9,7 @@ declare var __ESM_BUNDLER__: boolean declare var __ESM_BROWSER__: boolean declare var __CJS__: boolean declare var __SSR__: boolean +declare var __VUE_SSR_SETTERS__: Array<(v: boolean) => void> declare var __COMMIT__: string declare var __VERSION__: string declare var __COMPAT__: boolean diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index 7a800949eea..5696964671d 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -373,6 +373,56 @@ describe('api: watch', () => { expect(dummy).toBe(0) }) + it('stopping the watcher (SSR)', async () => { + type SetBoolean = (v: boolean) => void + const setSSR = (ssr: boolean) => { + __SSR__ = ssr + __VUE_SSR_SETTERS__.forEach((setInSSRSetupState: SetBoolean) => { + setInSSRSetupState(ssr) + }) + } + setSSR(true) + + let dummy = 0 + const count = ref(1) + const captureValue = (value: number) => { + dummy = value + } + const watchCallback = vi.fn(newValue => { + captureValue(newValue) + }) + const scenario = () => { + const Comp = defineComponent({ + created() { + const getter = () => this.count + captureValue(getter()) // sets dummy to 1 + const stop = this.$watch(getter, watchCallback) + stop() + this.count = 2 // shouldn't trigger side effect + }, + render() { + return h('div', this.count) + }, + setup() { + return { count } + }, + }) + const root = nodeOps.createElement('div') + render(h(Comp), root) + } + + expect(scenario).not.toThrowError(/stop is not a function/) + expect(watchCallback).not.toHaveBeenCalled() + expect(dummy).toBe(1) + await nextTick() + count.value = 3 // shouldn't trigger side effect + await nextTick() + expect(watchCallback).not.toHaveBeenCalled() + expect(dummy).toBe(1) + + setSSR(false) + }) + it('stopping the watcher (with source)', async () => { const state = reactive({ count: 0 }) let dummy diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index a14823beb62..798b6e7261b 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -179,11 +179,11 @@ function doWatch( // immediately watch or watchEffect baseWatchOptions.once = true } else { - return { - stop: NOOP, - resume: NOOP, - pause: NOOP, - } as WatchHandle + const watchStopHandle = () => {} + watchStopHandle.stop = NOOP + watchStopHandle.resume = NOOP + watchStopHandle.pause = NOOP + return watchStopHandle } }