reactive.js
import { hasChanged, isArray, isObject } from "../utils";
import {track, trigger } from "./effect";
const proxyMap = new WeakMap()
export function reactive(target){
if(!isObject(target)){
return target;
}
if(isReactive(target)){
return target
}
if(proxyMap.has(target)){
return proxyMap.get(target)
}
const proxy = new Proxy(target,{
get(target,key,receiver){
if(key === '__isReactive'){
return true
}
const res = Reflect.get(target,key,receiver);
track(target,key)
return isObject(res)?reactive(res):res;
},
set(target,key,value,receiver){
let oldLength = target.length;
const oldValue = target[key];
const res = Reflect.set(target,key,value,receiver);
if(hasChanged(oldValue,value)){
trigger(target,key);
if(isArray(target)&& hasChanged(oldLength,target.length)){
trigger(target,'length');
}
}
return res
}
})
proxyMap.set(target,proxy)
return proxy
}
export function isReactive(target){
return !!(target && target.__isReactive);
}
effect.js
// 可能会有多个effect []
const effectStack = [];
let activeEffect; //使两个函数有关系 用来记录当前正在执行的副作用函数
export function effect(fn,options={}){
// 接收的为一段用户代码 可能会出错 所以使用try进行包裹来处理
// try-catch-finally的执行顺序是:1、不管有没有出现异常,finally块中的代码都会执行;
// 2、当try和catch中有return时,finally仍然会执行;
// 3、finally是在return后面的表达式运算后执行的。
// options里的参数 lazy 判断依赖有没有更新 需不需要出发effect函数 scheduler 调度机制
const effectFn= ()=>{
try{
activeEffect = effectFn; //复制为当前执行的副作用函数
effectStack.push(activeEffect)
return fn();
}finally{
//todo 执行完后应该让它还原
effectStack.pop();
activeEffect = effectStack[effectStack.length-1];
}
}
if(!options.lazy){
effectFn();
}
effectFn.scheduler = options.scheduler;
return effectFn;
}
// ##targetMap
// targetMap用于存储副作用,并建立副作用和依赖的对应关系。
// 一个副作用可能依赖多个响应式对象,也可能依赖一个响应式里的多个属性。
// 而一个属性又可能被多个副作用依赖,因此targetMap的结构设计如下。
//就是会有多个effect里面又会有多个对象包对象的响应式结构
// ```javascript
// { // 这是一个WeakMap
// [target]: { // key是reactiveObject, value是一个Map
// [key]: [] // key是reactiveObject的键值, value是一个Set
// }
// }
// ```
// 使用WeakMap的原因:当reactiveObject不再使用后,不必手动去WeakMap里删除,垃圾回收系统可以自动回收。
const targetMap = new WeakMap();
export function track(target,key){
//收集依赖的时候必需要存在activeEffect
if(!activeEffect){
return ;
}
let depsMap = targetMap.get(target)
//第一次收集依赖可能不存在
if(!depsMap){
targetMap.set(target,(depsMap = new Map()))
}
let deps = depsMap.get(key)
if(!deps){
depsMap.set(key,(deps=new Set()))
}
//将副作用存储进去 并建立联系
deps.add(activeEffect)
}
// 相当于track的逆运算
export function trigger(target,key){
const depsMap = targetMap.get(target);
if(!depsMap){
return
}
const deps = depsMap.get(key);
if(!deps){
return
}
deps.forEach((effectFn) => {
if(effectFn.scheduler){
effectFn.scheduler(effectFn)
}else{
effectFn()
}
});
}
ref.js
import { track, trigger } from "./effect";
import { reactive } from './reactive';
// ref 也是响应式数据的一种,类似于 reactive,不过通常用 ref 封装简单数据,如 Number、String、Boolean等,用 reactive 只能封装对象
export function ref(value){
if(isRef(value)){
return value;
}
return new RefImpl(value)
}
export function isRef(value){
return !!(value &&value.__isRef)
}
// ref 中的数据实际上是储存在 this._value 中,通过 value 来进行存取是因为类上定义了 value 的 getter 和 setter,而这两者维护的都是其中的 _value 属性,是一种类似代理的关系
class RefImpl{
constructor(value){
this.__isRef = true;
this._value =convert(value) ;
}
get value(){
track(this,'value')
return this._value;
}
set value(newValue){
//如果发生改变了 才去更新_value属性
if(hasChanged(newValue,this._value)){
this._value = convert(newValue);
trigger(this,'value')
}
}
}
function convert(value){
return isObject(value)?reactive(value):value;
}
computed.js
import { effect, track, trigger } from "./effect";
export function computed(getterOrOption){
//这是处理当computed处理当传入的为get函数 和set函数的处理
let getter,setter;
if(isFunction(getterOrOption)){
getter = getterOrOption;
setter=()=>{
console.warn('computed is readonly')
}
}else{
getter = getterOrOption.get;
setter = getterOrOption.set;
}
return new ComputedImpl(getter,setter)
}
class ComputedImpl {
constructor(getter,setter){
this._setter= setter;
this._value = undefined;
//依赖有没有更新
this._dirty = true;
this.effect= effect(getter,{
lazy:true,
//调度机制 为了让effect 触发更新时不是去立即执行 而是先触发scheduler
scheduler:()=>{
if(!this._dirty){
this._dirty = true;
trigger(this,'value')
}
}
})
}
get value (){
// 执行了一次 getter 之后,_dirty 置为 false,因此在下次执行
// getter 时就不会再进行计算而是直接返回,而触发调度函数之后,_dirty 置为 true,
// 再次执行 getter 的话就会再次进行计算,然后再置为 false,如此往复
if (this._dirty){
//依赖更新 需要重新计算
this._value=this.effect();
this._dirty = false;
track(this,'value')
}
return this._value;
}
set value (newValue){
//todo
this._setter(newValue)
}
}