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
|
||||
return this.fn() //完成第一次getter行为的触发
|
||||
}
|
||||
stop() {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -8,6 +8,10 @@ import { isObject } from '@vue/shared'
|
||||
*/
|
||||
export const reactiveMap = new WeakMap<object, any>()
|
||||
|
||||
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 = <T extends unknown>(value: T): T =>
|
||||
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 { watch } from './apiWatch'
|
||||
|
||||
@@ -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 } = {}
|
||||
|
||||
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 { queuePreFlushCb } from '@vue/runtime-core'
|
||||
export { queuePreFlushCb, watch } from '@vue/runtime-core'
|
||||
|
||||
Reference in New Issue
Block a user