简单的vue响应式

88 阅读2分钟

简单的reactive

最近在项目中使用vue3 看到了一个讲解reactive的视频 顺便记录一下

需要了解前置知识

Proxy,Set,Map,WeakMap,Reflect

直接开始吧

创建 targetMap activeEffect effect

   const targetMap = new WeakMap()// targetMap 存储每个对象在更新时应重新运行的效果
   let activeEffect = null
   function effect(eff) {
      activeEffect = eff;
      activeEffect();
      activeEffect = null;
}

创建一个track函数追踪对象

// 接收一个对象 及对象的key
 function track(target,key) {
 if(activeEffect){
      let depsMap = targetMap.get(target)// weakMap 的键值必须是对象, 拿到对象的
     if(!depsMap){// 如果这个对象没有被监听
         targetMap.set(target,(depsMap= new Map()))// targetMap添加当前对象为key value 为一个 map
     }
     let dep = depsMap.get(key)//获取监听到的key
     if(!dep) { // key 未被监听追踪
        // 将追踪的key 设置为一个 new Set
        depsMap.set(key,(deps= new Set()))
     }
         dep.add(activeEffect)// 添加一个依赖函数
     }
 }

在创建一个trigger函数 触发

// 接收一个 target(触发的对象)  触发的key 对象属性
   function trigger(target,key){
       const depsMap = targetMap.get(target)
       if(!depsMap) {// 此对象是否被追踪
          return;
       }
       let dep = depsMap.get(key)
       if(dep){// 执行依赖的函数
          dep.forEach(effect => effect())
       }
   }

创建一个reactive 函数

需要用到 Proxy

   function reactive(target) {
      const handler = {
          get(target,key,receiver){
             let result = Reflect.get(target,key,receiver)
           // 调用 track 
             track(target,key)
             return result
          },
          set(target,key,value,receiver){
             let oldValue = target[key]
             let result = Reflect.set(target,key,value,receiver)
             if(result && oldValue !== value){// 当改变对象的key
             // 调用 trigger
               trigger(target,key)
             }
             return result
          }
     }
     return new Proxy(target,handler)
  }

测试一下

   let product = reactive({
        price:2,
        quantity:5
   })
   let total = 0
   effect(()=>{
      total = product.price * product.quantity
   })
   console.log(total) // 10
   product.price = 3
   console.log(total) // 15

衍生一下 ref computed

ref 可以包装任意的类型 ref的使用 需要.value 需要用到对象的get set


function ref(raw){
   let r = {
      get value(){
      track(r, "value");
      return raw
      },
      set value(val) {
         if(val !== raw)
         raw = val
         trigger(r, "value");
      }
   }
   return r
}
function computed(fn) {
  let result = ref();
  effect(() => {
    result.value = fn();
  });
  return result;
}

一个简单的响应式就完成了

参考Vue 3响应式原理 (Vue 3 Reactivity)【中英字幕】- Vue Mastery