From b9a9c523335cef6e884a3e1b37e11181784e6f61 Mon Sep 17 00:00:00 2001 From: dj <1042039504@qq.com> Date: Wed, 25 Feb 2026 22:15:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(watch):=20=E5=AE=9E=E7=8E=B0watch=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=B9=B6=E5=AE=8C=E5=96=84=E5=93=8D=E5=BA=94=E5=BC=8F?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增apiWatch.ts实现watch功能,支持immediate和deep选项 - 扩展ReactiveEffect类添加stop方法用于停止监听 - 导出ReactiveEffect和isReactive函数供外部使用 - 添加ReactiveFlags枚举和IS_REACTIVE标识符 - 在reactive对象上添加__v_isReactive标识 - 导出EMPTY_OBJ常量用于默认参数 - 添加watch功能到Vue入口文件 - 创建watch.html示例验证监听功能正常工作 --- packages/reactivity/src/effect.ts | 1 + packages/reactivity/src/index.ts | 4 +- packages/reactivity/src/reactive.ts | 11 +++- packages/runtime-core/src/apiWatch.ts | 60 +++++++++++++++++++++ packages/runtime-core/src/index.ts | 1 + packages/shared/src/index.ts | 2 + packages/vue/examples/reactivity/watch.html | 26 +++++++++ packages/vue/src/index.ts | 2 +- 8 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 packages/runtime-core/src/apiWatch.ts create mode 100644 packages/vue/examples/reactivity/watch.html diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index bf3a5fd..9f30aa9 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -41,6 +41,7 @@ export class ReactiveEffect { activeEffect = this return this.fn() //完成第一次getter行为的触发 } + stop() {} } /** diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index 7964e45..0ce2c70 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -1,4 +1,4 @@ -export { reactive } from './reactive' -export { effect } from './effect' +export { reactive, isReactive } from './reactive' +export { effect, ReactiveEffect } from './effect' export { ref } from './ref' export { computed } from './computed' diff --git a/packages/reactivity/src/reactive.ts b/packages/reactivity/src/reactive.ts index 2ed7ec0..e585f1e 100644 --- a/packages/reactivity/src/reactive.ts +++ b/packages/reactivity/src/reactive.ts @@ -8,6 +8,10 @@ import { isObject } from '@vue/shared' */ export const reactiveMap = new WeakMap() +export const enum ReactiveFlags { + IS_REACTIVE = '__v_isReactive' +} + /** * 为复杂数据类型, 创建响应式对象 * @param target 被代理的对象 @@ -35,7 +39,8 @@ function createReactiveObject( } //未被代理则生成proxy实例 const proxy = new Proxy(target, baseHandlers) - + //添加reactive标识 + proxy[ReactiveFlags.IS_REACTIVE] = true //缓存代理对象 proxyMap.set(target, proxy) return proxy @@ -43,3 +48,7 @@ function createReactiveObject( export const toReactive = (value: T): T => isObject(value) ? reactive(value as object) : value + +export function isReactive(value): boolean { + return !!(value && value[ReactiveFlags.IS_REACTIVE]) +} diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts new file mode 100644 index 0000000..7a5e5c4 --- /dev/null +++ b/packages/runtime-core/src/apiWatch.ts @@ -0,0 +1,60 @@ +import { EMPTY_OBJ, hasChanged } from '@vue/shared' +import { isReactive, ReactiveEffect } from '@vue/reactivity' +import { queuePreFlushCb } from '@vue/runtime-core' + +export interface WatchOptions { + immediate?: immediate + deep?: boolean +} + +export function watch(source, cb: Function, options?: WatchOptions) { + return doWatch(source, cb, options) +} + +function doWatch( + source, + cb: Function, + { immediate, deep }: WatchOptions = EMPTY_OBJ +) { + let getter: () => any + if (isReactive(source)) { + getter = () => source + deep = true + } else { + getter = () => {} + } + //处理deep的状态 + if (cb && deep) { + //todo + //浅拷贝的形式--指向同样的内存空间 + const baseGetter = getter + getter = () => baseGetter() + } + + let oldValue = {} + //每次执行等同于watch被触发一次 + const job = () => { + if (cb) { + const newValue = effect.run() + if (deep || hasChanged(newValue, oldValue)) { + cb(newValue, oldValue) + oldValue = newValue + } + } + } + //调度器 + let scheduler = () => queuePreFlushCb(job) + const effect = new ReactiveEffect(getter, scheduler) + if (cb) { + if (immediate) { + job() + } else { + oldValue = effect.run() + } + } else { + effect.run() + } + return () => { + effect.stop() + } +} diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index a3a2657..c1885a8 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -1 +1,2 @@ export { queuePreFlushCb } from './scheduler' +export { watch } from './apiWatch' diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 5494e3b..f740520 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -15,3 +15,5 @@ export const isFunction = (val: unknown): val is Function => { } export const extend = Object.assign + +export const EMPTY_OBJ: { readonly [key: string]: any } = {} diff --git a/packages/vue/examples/reactivity/watch.html b/packages/vue/examples/reactivity/watch.html new file mode 100644 index 0000000..1340c0f --- /dev/null +++ b/packages/vue/examples/reactivity/watch.html @@ -0,0 +1,26 @@ + + + + + Document + + + +
+ + + \ No newline at end of file diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 8fb36c5..08ac420 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -1,2 +1,2 @@ export { reactive, effect, ref, computed } from '@vue/reactivity' -export { queuePreFlushCb } from '@vue/runtime-core' +export { queuePreFlushCb, watch } from '@vue/runtime-core'