实现vue3的 reactive,computed和watch

用reactive实现computed和watch

  // 存储副作用函数的桶
  const bucket = new WeakMap()
  // 充当for in操作时候 对象的key
  const ITERATE_KEY = Symbol()

  // 实现reactive,创建对象的代理
  function reactive(obj) {
    return new Proxy(obj, {
    
      // 拦截读取操作
      get(target, key, receiver) {
        if (key === 'raw') {
          return target
        }
        // 将副作用函数 activeEffect 添加到存储副作用函数的桶中
        track(target, key)
        // 返回属性值
        return Reflect.get(target, key, receiver)
      },
      
      // 拦截设置操作
      set(target, key, newVal, receiver) {
        const oldVal = target[key]
        // 如果属性不存在,则说明是在添加新的属性,否则是设置已存在的属性
        const type = Object.prototype.hasOwnProperty.call(target, key) ? 'SET' : 'ADD'

        // 设置属性值
        const res = Reflect.set(target, key, newVal, receiver)
        console.log(target === receiver.raw)
        if (target === receiver.raw) {
          if (oldVal !== newVal && (oldVal === oldVal || newVal === newVal)) {
            trigger(target, key, type)
          }
        }
        return res
      },
      
      // in操作符监听
      has(target, key) {
        track(target, key)
        return Reflect.has(target, key)
      },
      
      // for in操作符监听
      ownKeys(target) {
        track(target, ITERATE_KEY)
        return Reflect.ownKeys(target)
      },
      
      // delete操作符监听
      deleteProperty(target, key) {
        const hadKey = Object.prototype.hasOwnProperty.call(target, key)
        const res = Reflect.deleteProperty(target, key)

        if (res && hadKey) {
          trigger(target, key, 'DELETE')
        }

        return res
      }
    })
  }


// 收集代理副作用函数
  function track(target, key) {
    if (!activeEffect) return
    let depsMap = bucket.get(target)
    if (!depsMap) {
      bucket.set(target, (depsMap = new Map()))
    }
    let deps = depsMap.get(key)
    if (!deps) {
      depsMap.set(key, (deps = new Set()))
    }
    deps.add(activeEffect)
    activeEffect.deps.push(deps)
  }

// 触发执行副作用函数
  function trigger(target, key, type) {
    const depsMap = bucket.get(target)
    if (!depsMap) return
    const effects = depsMap.get(key)

    const effectsToRun = new Set()
    effects && effects.forEach(effectFn => {
      if (effectFn !== activeEffect) {
        effectsToRun.add(effectFn)
      }
    })

    // console.log(type, key)

    if (type === 'ADD' || type === 'DELETE') {
      const iterateEffects = depsMap.get(ITERATE_KEY)
      iterateEffects && iterateEffects.forEach(effectFn => {
        if (effectFn !== activeEffect) {
          effectsToRun.add(effectFn)
        }
      })
    }

    effectsToRun.forEach(effectFn => {
      if (effectFn.options.scheduler) {
        effectFn.options.scheduler(effectFn)
      } else {
        effectFn()
      }
    })
  }

  // 用一个全局变量存储当前激活的 effect 函数
  let activeEffect
  // effect 栈
  const effectStack = []


// 将组副作用函数收集触发track函数
  function effect(fn, options = {}) {
    const effectFn = () => {
      cleanup(effectFn)
      // 当调用 effect 注册副作用函数时,将副作用函数复制给 activeEffect
      activeEffect = effectFn
      // 在调用副作用函数之前将当前副作用函数压栈
      effectStack.push(effectFn)
      const res = fn()
      // 在当前副作用函数执行完毕后,将当前副作用函数弹出栈,并还原 activeEffect 为之前的值
      effectStack.pop()
      activeEffect = effectStack[effectStack.length - 1]

      return res
    }
    // 将 options 挂在到 effectFn 上
    effectFn.options = options
    // activeEffect.deps 用来存储所有与该副作用函数相关的依赖集合
    effectFn.deps = []
    // 执行副作用函数
    if (!options.lazy) {
      effectFn()
    }

    return effectFn
  }

// 解决副作用汉同时读取和设置同一个对象的属性,造成的无限循环
  function cleanup(effectFn) {
    for (let i = 0; i < effectFn.deps.length; i++) {
      const deps = effectFn.deps[i]
      deps.delete(effectFn)
    }
    effectFn.deps.length = 0
  }


// computed 的实现
  function computed(getter) {
    let value // 缓存值
    let dirty = true // true表示getter中的多个数据,改变了,需要重新计算

    const effectFn = effect(getter, {
      lazy: true,
      scheduler() {
        console.log("执行")
        if (!dirty) {
          dirty = true
          trigger(newCompute, 'value')
        }
      }
    })
    const newCompute = {
      get value() { // 第一个获取computed值
        if (dirty) {
          value = effectFn();// getter收集到各自的副作用函数
          dirty = false
        }
        track(newCompute, 'value');// 用来收集计算属性的值的代理
        return value
      }
    }

    return newCompute // 第一次读取,触发
  }

  
  // 测试
  
  // 定义obj为响应式数据
  const obj=reactive({foo:1,bar:0})
  

  const sumRes = computed(() => obj.foo + obj.bar);//注意sumRes不是响应式

  console.log("computed1",sumRes.value)
  console.log("computed2",sumRes.value);// 执行

  obj.foo++ // 执行执行 trigger(newCompute, 'value') 如果没有下面effect,其实和computed没关系

  console.log("computed3",sumRes.value);// 重新执行副作用函数,获取value,值改变

  // 这个时候修改sumRes,不会触发任何效果,修改没有用
  sumRes.value=11;

  console.log("computed4",sumRes.value);// 重新执行副作用函数,获取value,值改变
  effect(() => {
    console.log(sumRes.value)
  })

  obj.foo++ // 这个时候修改obj,sumRes才会执行副作用函数才有效




  ///


   // 实现watch
  // =========================

  // 递归读取对象所有的属性,触发track函数代理对象全部属性
  function traverse(value, seen = new Set()) {
    if (typeof value !== 'object' || value === null || seen.has(value)) return
    seen.add(value)
    for (const k in value) {
      traverse(value[k], seen)
    }

    return value
  }

  function watch(source, cb, options = {}) {
    let getter
    // 如果是监听对象的函数,直接为getter,注意监听函数必须是读取数据
    if (typeof source === 'function') {
      getter = source
    } else {
      getter = () => traverse(source)
    }

    let oldValue, newValue

    let cleanup
    function onInvalidate(fn) { // 解决竞态问题
      cleanup = fn
    }

    const job = () => {
      newValue = effectFn()
      if (cleanup) {
        cleanup()
      }
      cb(oldValue, newValue, onInvalidate)
      oldValue = newValue
    }

    const effectFn = effect(
      // 执行 getter
      () => getter(), // 副作用函数
      {
        lazy: true,
        scheduler: () => {
          if (options?.flush === 'post') { //表示其他副作用函数执行之后再执行
            const p = Promise.resolve()
            p.then(job)
          } else {
            job()
          }
        }
      }
    )
    if (options?.immediate) {
      job()
    } else {
      oldValue = effectFn()
    }
  }
  
  

  // 异步测试函数
  let count = 0
  function fetch() {
    count++
    const res = count === 1 ? 'A' : 'B'
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(res)
      }, count === 1 ? 1000 : 100);
    })
  }

  let finallyData

  watch(() => obj.foo, async (newVal, oldVal, onInvalidate) => {
    let valid = true
    onInvalidate(() => {
      valid = false
    })
    const res = await fetch()

    if (!valid) return

    finallyData = res
    console.log(finallyData)
  })

  obj.foo++
  setTimeout(() => {
    obj.foo++
  }, 200);


//1.以上代码没有实现调度器,
// 调度器是指多次修改某一个对象的数据,只执行一次副作用函数(渲染函数),这上面的代码只要修改,就会触发渲染函数
//2.没有实现重写数组方法,代理数组,map,set等数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值