// 因为vue3中不再是将数据定义到data中就可以实现响应式,而是使用reactive或ref
// WeakMap 的 key 只能是 Object 类型。
let targetMap = WeakMap();
let activeEffect;
/**
* {
* target:{
* key: [ReactiveEffect,ReactiveEffect,....]
* }
* }
*/
// 收集依赖
function track(target, key) {
// 判断该值是否收集,未收集就添加
let depsMap = targetMap.get(target);
if (!depsMap) targetMap.set(depsMap, (depsMap = new Map()));
// 判断depsMap 中有没有key
let dep = depsMap.get(key);
if (!dep) depsMap.set(key, (dep = new Set()));
trackEffect(dep);
}
//
function trackEffect(dep) {
// 相当于 vue2中的, Dep.target && dep.add(Dep.target)
if (dep.has(activeEffect)) dep.add(activeEffect)
}
// 触发
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
// effect 上面肯定有一个 run 方法。
depsMap.get(key).forEach(effect => effect && effect.run())
}
/**
备注1: Reflect
Reflect是一个内建的对象,用来提供方法去拦截JavaScript的操作。Reflect不是一个函数对象,所以它是不可构造的,也就是说它不是一个构造器,你不能通过`new`操作符去新建或者将其作为一个函数去调用Reflect对象。Reflect的所有属性和方法都是静态的。
1.现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。
2.修改某些Object方法的返回结果,让其变得更规范化。如Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。让Object操作都变成函数行为。
3.Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。
*/
//进行判断是否是对象
function isObject(data) {
return data && typeof data === 'object'
}
export function reactive(data) {
if (!isObject(data)) return;
// Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
// https://www.bookstack.cn/read/es6-3rd/spilt.1.docs-proxy.md Proxy 支持的拦截操作,一共 13 种
// 简化createReactiveObject
return new Proxy(data, {
// Reflect.get()方法与从 对象 (target[propertyKey]) 中读取属性类似,但它是通过一个函数执行来操作的。
// 因备注1.1,在继承可能会出现问题 因此这里不使用target[key]
get(target, key, reactiver) {
const ret = Reflect.get(target, key, reactiver);
track(target, key);
return isObject(ret) ? reactive(ret) : ret;
},
// Reflect.set() 工作方式就像在一个对象上设置一个属性。
set(target, key, value, reactiver) {
Reflect.set(target, key, value, reactiver);
trigger(target, key);
return true;
},
// 拦截delete操作,如果这个方法抛出错误,或者返回false,则当前属性就无法被delete命令删除
deleteProperty(target, key) {
trigger(target, key);
return Reflect.defineProperty(target, key);
},
//拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效,典型的就是in操作符
//如果原对象不可配置或者禁止扩展,这时has拦截会报错。
//值得注意的是,has方法拦截的是HasProperty操作,而不是HasOwnProperty操作,即has方法不判断一个属性是对象自身的属性,还是继承的属性。
has(target, key) {
track(target, key);
return Reflect.has(target, key);
},
//拦截对象自身属性的读取操作,具体拦截以下操作:object.getOwnPropertyNames(), object.getOwnPropertySymbols(), object.keys()
ownKeys(target, key) {
track(target, key);
return Reflect.ownKeys(target, key)
}
})
}
export function ref(init) {
class RefImpl {
constructor(init) {
this.__value = init;
}
get value() {
// trackRefValue源码方法,为了方便现全部使用track
track(this, "value");
return this.__value;
}
set value(newValue) {
this.__value = newValue;
trigger(this, 'value');
}
}
return new RefImpl(init)
}
// 在定义一个 effect 的函数中,第一个参数是一个函数;
// 如果这个函数中,有使用 ref/reactive
// effect(() => {
// console.log(num.value)
// })
function effect(fn, options = {}) {
let __effect = new ReactiveEffect(fn);
if (!options.lazy) {
__effect.run();
}
return __effect;
}
// 计算属性监听
export function computed(fn) {
// 只考虑函数的情况
let __computed;
const e = effect(fn, { lazy: true });
__computed = {
get value() {
return e.run();
}
}
return __computed;
}
export function mount(instance, el) {
effect(function () {
instance.$data && update(instance, el);
})
instance.$data = instance.setup()
update(instance, el);
function update(instance, el) {
el.innerHTML = instance.render();
}
}
class ReactiveEffect {
constructor(fn) {
this.fn = fn;
}
run() {
activeEffect = this;
return this.fn();
}
}
评论