feat(watch): 实现watch功能并完善响应式系统
- 新增apiWatch.ts实现watch功能,支持immediate和deep选项 - 扩展ReactiveEffect类添加stop方法用于停止监听 - 导出ReactiveEffect和isReactive函数供外部使用 - 添加ReactiveFlags枚举和IS_REACTIVE标识符 - 在reactive对象上添加__v_isReactive标识 - 导出EMPTY_OBJ常量用于默认参数 - 添加watch功能到Vue入口文件 - 创建watch.html示例验证监听功能正常工作
This commit is contained in:
@@ -41,6 +41,7 @@ export class ReactiveEffect<T = any> {
|
|||||||
activeEffect = this
|
activeEffect = this
|
||||||
return this.fn() //完成第一次getter行为的触发
|
return this.fn() //完成第一次getter行为的触发
|
||||||
}
|
}
|
||||||
|
stop() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export { reactive } from './reactive'
|
export { reactive, isReactive } from './reactive'
|
||||||
export { effect } from './effect'
|
export { effect, ReactiveEffect } from './effect'
|
||||||
export { ref } from './ref'
|
export { ref } from './ref'
|
||||||
export { computed } from './computed'
|
export { computed } from './computed'
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import { isObject } from '@vue/shared'
|
|||||||
*/
|
*/
|
||||||
export const reactiveMap = new WeakMap<object, any>()
|
export const reactiveMap = new WeakMap<object, any>()
|
||||||
|
|
||||||
|
export const enum ReactiveFlags {
|
||||||
|
IS_REACTIVE = '__v_isReactive'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 为复杂数据类型, 创建响应式对象
|
* 为复杂数据类型, 创建响应式对象
|
||||||
* @param target 被代理的对象
|
* @param target 被代理的对象
|
||||||
@@ -35,7 +39,8 @@ function createReactiveObject(
|
|||||||
}
|
}
|
||||||
//未被代理则生成proxy实例
|
//未被代理则生成proxy实例
|
||||||
const proxy = new Proxy(target, baseHandlers)
|
const proxy = new Proxy(target, baseHandlers)
|
||||||
|
//添加reactive标识
|
||||||
|
proxy[ReactiveFlags.IS_REACTIVE] = true
|
||||||
//缓存代理对象
|
//缓存代理对象
|
||||||
proxyMap.set(target, proxy)
|
proxyMap.set(target, proxy)
|
||||||
return proxy
|
return proxy
|
||||||
@@ -43,3 +48,7 @@ function createReactiveObject(
|
|||||||
|
|
||||||
export const toReactive = <T extends unknown>(value: T): T =>
|
export const toReactive = <T extends unknown>(value: T): T =>
|
||||||
isObject(value) ? reactive(value as object) : value
|
isObject(value) ? reactive(value as object) : value
|
||||||
|
|
||||||
|
export function isReactive(value): boolean {
|
||||||
|
return !!(value && value[ReactiveFlags.IS_REACTIVE])
|
||||||
|
}
|
||||||
|
|||||||
60
packages/runtime-core/src/apiWatch.ts
Normal file
60
packages/runtime-core/src/apiWatch.ts
Normal file
@@ -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 = boolean> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
export { queuePreFlushCb } from './scheduler'
|
export { queuePreFlushCb } from './scheduler'
|
||||||
|
export { watch } from './apiWatch'
|
||||||
|
|||||||
@@ -15,3 +15,5 @@ export const isFunction = (val: unknown): val is Function => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const extend = Object.assign
|
export const extend = Object.assign
|
||||||
|
|
||||||
|
export const EMPTY_OBJ: { readonly [key: string]: any } = {}
|
||||||
|
|||||||
26
packages/vue/examples/reactivity/watch.html
Normal file
26
packages/vue/examples/reactivity/watch.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const { reactive,watch } = Vue
|
||||||
|
const obj=reactive({
|
||||||
|
name:'张三'
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(obj,(value,oldValue)=>{
|
||||||
|
console.log('watch 监听被触发---value',value);
|
||||||
|
},{
|
||||||
|
immediate:true
|
||||||
|
})
|
||||||
|
setTimeout(()=>{
|
||||||
|
obj.name='李四'
|
||||||
|
},2000)
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export { reactive, effect, ref, computed } from '@vue/reactivity'
|
export { reactive, effect, ref, computed } from '@vue/reactivity'
|
||||||
export { queuePreFlushCb } from '@vue/runtime-core'
|
export { queuePreFlushCb, watch } from '@vue/runtime-core'
|
||||||
|
|||||||
Reference in New Issue
Block a user