Skip to content

Commit d6af20e

Browse files
committed
watch/watchEffect making the apis inline with vue3
1 parent 6f58691 commit d6af20e

File tree

9 files changed

+556
-1130
lines changed

9 files changed

+556
-1130
lines changed

src/apis/effect.ts

Lines changed: 0 additions & 54 deletions
This file was deleted.

src/apis/watch.ts

Lines changed: 81 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,53 @@ import { assert, logError, noopFn, warn } from '../utils';
44
import { defineComponentInstance } from '../helper';
55
import { getCurrentVM, getCurrentVue } from '../runtimeContext';
66
import { WatcherPreFlushQueueKey, WatcherPostFlushQueueKey } from '../symbols';
7+
import { ComputedRef } from './computed';
78

8-
type CleanupRegistrator = (invalidate: () => void) => void;
9+
export type WatchEffect = (onInvalidate: InvalidateCbRegistrator) => void;
910

10-
type SimpleEffect = (onCleanup: CleanupRegistrator) => void;
11+
export type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T);
1112

12-
type StopHandle = () => void;
13-
14-
type WatcherCallBack<T> = (newVal: T, oldVal: T, onCleanup: CleanupRegistrator) => void;
15-
16-
type WatcherSource<T> = Ref<T> | (() => T);
13+
export type WatchCallback<V = any, OV = any> = (
14+
value: V,
15+
oldValue: OV,
16+
onInvalidate: InvalidateCbRegistrator
17+
) => any;
1718

1819
type MapSources<T> = {
19-
[K in keyof T]: T[K] extends WatcherSource<infer V> ? V : never;
20+
[K in keyof T]: T[K] extends WatchSource<infer V> ? V : never;
2021
};
2122

22-
type FlushMode = 'pre' | 'post' | 'sync';
23+
type MapOldSources<T, Immediate> = {
24+
[K in keyof T]: T[K] extends WatchSource<infer V>
25+
? Immediate extends true
26+
? (V | undefined)
27+
: V
28+
: never;
29+
};
2330

24-
interface WatcherOption {
25-
lazy: boolean; // whether or not to delay callcack invoking
26-
deep: boolean;
27-
flush: FlushMode;
28-
}
31+
type InvalidateCbRegistrator = (cb: () => void) => void;
32+
33+
type FlushMode = 'pre' | 'post' | 'sync';
2934

3035
export interface VueWatcher {
3136
lazy: boolean;
3237
get(): any;
3338
teardown(): void;
3439
}
3540

41+
export interface BaseWatchOptions {
42+
flush?: FlushMode;
43+
// onTrack?: ReactiveEffectOptions['onTrack'];
44+
// onTrigger?: ReactiveEffectOptions['onTrigger'];
45+
}
46+
47+
export interface WatchOptions<Immediate = boolean> extends BaseWatchOptions {
48+
immediate?: Immediate;
49+
deep?: boolean;
50+
}
51+
52+
export type StopHandle = () => void;
53+
3654
let fallbackVM: ComponentInstance;
3755

3856
function flushPreQueue(this: any) {
@@ -54,17 +72,28 @@ function installWatchEnv(vm: any) {
5472
vm.$on('hook:updated', flushPostQueue);
5573
}
5674

57-
function getWatcherOption(options?: Partial<WatcherOption>): WatcherOption {
75+
function getWatcherOption(options?: Partial<WatchOptions>): WatchOptions {
5876
return {
5977
...{
60-
lazy: false,
78+
immediate: false,
6179
deep: false,
6280
flush: 'post',
6381
},
6482
...options,
6583
};
6684
}
6785

86+
function getWatchEffectOption(options?: Partial<WatchOptions>): WatchOptions {
87+
return {
88+
...{
89+
immediate: true,
90+
deep: false,
91+
flush: 'sync',
92+
},
93+
...options,
94+
};
95+
}
96+
6897
function getWatcherVM() {
6998
let vm = getCurrentVM();
7099
if (!vm) {
@@ -141,14 +170,14 @@ function createVueWatcher(
141170

142171
function createWatcher(
143172
vm: ComponentInstance,
144-
source: WatcherSource<unknown> | WatcherSource<unknown>[] | SimpleEffect,
145-
cb: WatcherCallBack<any> | null,
146-
options: WatcherOption
173+
source: WatchSource<unknown> | WatchSource<unknown>[] | WatchEffect,
174+
cb: WatchCallback<any> | null,
175+
options: WatchOptions
147176
): () => void {
148177
const flushMode = options.flush;
149178
const isSync = flushMode === 'sync';
150179
let cleanup: (() => void) | null;
151-
const registerCleanup: CleanupRegistrator = (fn: () => void) => {
180+
const registerCleanup: InvalidateCbRegistrator = (fn: () => void) => {
152181
cleanup = () => {
153182
try {
154183
fn();
@@ -180,10 +209,10 @@ function createWatcher(
180209

181210
// effect watch
182211
if (cb === null) {
183-
const getter = () => (source as SimpleEffect)(registerCleanup);
212+
const getter = () => (source as WatchEffect)(registerCleanup);
184213
const watcher = createVueWatcher(vm, getter, noopFn, {
185-
noRun: true, // take control the initial gettet invoking
186-
deep: options.deep,
214+
noRun: true, // take control the initial getter invoking
215+
deep: options.deep || false,
187216
sync: isSync,
188217
before: runCleanup,
189218
});
@@ -220,7 +249,7 @@ function createWatcher(
220249
cb(n, o, registerCleanup);
221250
};
222251
let callback = createScheduler(applyCb);
223-
if (!options.lazy) {
252+
if (options.immediate) {
224253
const originalCallbck = callback;
225254
// `shiftCallback` is used to handle the first sync effect run.
226255
// The subsequent callbacks will redirect to `callback`.
@@ -235,7 +264,7 @@ function createWatcher(
235264

236265
// @ts-ignore: use undocumented option "sync"
237266
const stop = vm.$watch(getter, callback, {
238-
immediate: !options.lazy,
267+
immediate: options.immediate,
239268
deep: options.deep,
240269
sync: isSync,
241270
});
@@ -246,38 +275,42 @@ function createWatcher(
246275
};
247276
}
248277

249-
export function watchEffect(
250-
effect: SimpleEffect,
251-
options?: Omit<Partial<WatcherOption>, 'lazy'>
252-
): StopHandle {
253-
const opts = getWatcherOption(options);
278+
export function watchEffect(effect: WatchEffect, options?: BaseWatchOptions): StopHandle {
279+
const opts = getWatchEffectOption(options);
254280
const vm = getWatcherVM();
255281
return createWatcher(vm, effect, null, opts);
256282
}
257283

258-
export function watch<T = any>(
259-
source: SimpleEffect,
260-
options?: Omit<Partial<WatcherOption>, 'lazy'>
261-
): StopHandle;
262-
export function watch<T = any>(
263-
source: WatcherSource<T>,
264-
cb: WatcherCallBack<T>,
265-
options?: Partial<WatcherOption>
284+
// overload #1: single source + cb
285+
export function watch<T, Immediate extends Readonly<boolean> = false>(
286+
source: WatchSource<T>,
287+
cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>,
288+
options?: WatchOptions<Immediate>
266289
): StopHandle;
267-
export function watch<T extends WatcherSource<unknown>[]>(
290+
291+
// overload #2: array of multiple sources + cb
292+
// Readonly constraint helps the callback to correctly infer value types based
293+
// on position in the source array. Otherwise the values will get a union type
294+
// of all possible value types.
295+
export function watch<
296+
T extends Readonly<WatchSource<unknown>[]>,
297+
Immediate extends Readonly<boolean> = false
298+
>(
268299
sources: T,
269-
cb: (newValues: MapSources<T>, oldValues: MapSources<T>, onCleanup: CleanupRegistrator) => any,
270-
options?: Partial<WatcherOption>
300+
cb: WatchCallback<MapSources<T>, MapOldSources<T, Immediate>>,
301+
options?: WatchOptions<Immediate>
271302
): StopHandle;
272-
export function watch(
273-
source: WatcherSource<unknown> | WatcherSource<unknown>[] | SimpleEffect,
274-
cb?: Partial<WatcherOption> | WatcherCallBack<any>,
275-
options?: Partial<WatcherOption>
303+
304+
// implementation
305+
export function watch<T = any>(
306+
source: WatchSource<T> | WatchSource<T>[],
307+
cb: WatchCallback<T>,
308+
options?: WatchOptions
276309
): StopHandle {
277-
let callback: WatcherCallBack<unknown> | null = null;
310+
let callback: WatchCallback<unknown> | null = null;
278311
if (typeof cb === 'function') {
279312
// source watch
280-
callback = cb as WatcherCallBack<unknown>;
313+
callback = cb as WatchCallback<unknown>;
281314
} else {
282315
// effect watch
283316
if (__DEV__) {
@@ -287,7 +320,7 @@ export function watch(
287320
`supports \`watch(source, cb, options?) signature.`
288321
);
289322
}
290-
options = cb as Partial<WatcherOption>;
323+
options = cb as Partial<WatchOptions>;
291324
callback = null;
292325
}
293326

src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,5 @@ export { getCurrentVM as getCurrentInstance } from './runtimeContext';
3737
export * from './apis/state';
3838
export * from './apis/lifecycle';
3939
export * from './apis/watch';
40-
export * from './apis/effect';
4140
export * from './apis/computed';
4241
export * from './apis/inject';

0 commit comments

Comments
 (0)