手写reactive2.0(依赖收集)

假如您也和我一样,在准备春招。欢迎加我微信shunwuyu,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!

在上篇文章中,我们手搓了一个丐帮的reactive,其实在对数据响应式处理的时候还需要考虑一些额外的逻辑,考虑到响应式数据不仅仅是查值改值这么简单外,还有一系列副作用函数的收集和触发,今天我们这篇文章会从依赖收集出发优化一下之前的手写reative代码。

优化版reactive

在之前丐帮的手写reactive我们使用Proxy代理对象的各种操作后只是简单地进行了读值该值,其实不然,我们还需要做一件很重要的事情------依赖收集(也称作副作用函数收集)!

何为依赖收集

我们都知道,一方有难八方支援是我们中国人美好的道德品质,也正是处于我们是同一种族的同胞,有着千丝万缕的关联。在这种关系的连接下,当一个函数触发影响到的响应式对象还会进而牵连许多相关的函数,比如computed和watch等等就是副作用函数,所以我们还需要对这些副作用函数进行收集。我们编程的响应式对象中的任意属性发生修改都应该将用到了这个属性的各个函数重新执行一遍。那么在此之前,需要知道哪些属性被用到了,也就是依赖收集,也称为副作用函数收集。

1. effect 函数创建副作用函数

effect函数是Vue3中实现响应式的核心之一,它的作用是创建一个响应式的副作用函数。这个副作用函数可以被当作watchcomputed等功能的基础。让我们看一下effect函数的主要实现:

js 复制代码
export function effect(fn, options={}) {
  const effectFn = () => {
    try {
      activeEffect = effectFn
      return fn()
    } finally {
      activeEffect = null
    }
  }
  if (!options.lazy) {
    effectFn()
  }
  effectFn.scheduler = options.scheduler
  return effectFn
}

在这段代码中,effect函数接收一个函数fn和一些配置项options。它内部创建了一个effectFn函数,通过activeEffect变量来追踪当前的副作用函数。在执行fn函数时,将activeEffect设置为当前的effectFn,从而在track函数中收集依赖。最后,将effectFnscheduler属性设置为传入的调度函数,并返回这个effectFn

2. track 函数收集副作用函数

track函数用于在属性被读取时收集副作用函数,以建立属性与副作用函数之间的关联关系。下面是track函数的实现:

js 复制代码
export function track(target, key) {
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    depsMap = new Map()
    targetMap.set(target, depsMap)
  }
  let deps = depsMap.get(key)
  if (!deps) {
    deps = new Set()
  }
  if (!deps.has(activeEffect) && activeEffect) {
    deps.add(activeEffect)
  }
  depsMap.set(key, deps)
}

track函数首先获取目标对象targettargetMap中的依赖映射depsMap,如果不存在,则创建一个新的映射。然后获取属性key对应的依赖集合deps,如果不存在,则创建一个新的集合。最后,将当前的副作用函数activeEffect加入到依赖集合中,并更新depsMap

当目标对象被读取值的时候我们就需要做副作用函数的收集

js 复制代码
function createGetter() {
  return function get(target, key, receiver) {
    const res = Reflect.get(target, key, receiver);
    track(target, key); // 收集依赖

    return res;
  }
}

这样,当属性被读取时,系统就能够追踪到使用该属性的副作用函数,并建立它们之间的关联关系。

3. trigger 函数触发副作用函数

trigger函数用于触发属性的副作用函数,即在属性被修改时执行相应的副作用函数。下面是trigger函数的主要实现:

js 复制代码
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()
    } else {
      effectFn()
    }
  });
}

trigger函数首先获取目标对象targettargetMap中的依赖映射depsMap,如果不存在,则直接返回。然后获取属性key对应的依赖集合deps,如果不存在,则直接返回。最后,遍历依赖集合,执行每个副作用函数,如果存在调度函数,则调用调度函数,否则直接执行副作用函数。

当目标对象有属性修改的时候我们就需要做副作用函数的触发,也就是通知所有使用当前属性的副作用函数进行更新

js 复制代码
function createSetter() {
  return function set(target, key, value, receiver) {
    const res = Reflect.set(target, key, value, receiver);
    trigger(target, key); // 触发依赖

    return res;
  }
}

这样,当属性被修改时,系统就能够找到所有使用该属性的副作用函数,通知它们进行更新操作,从而保持数据的响应性。

效果

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script type="module">
    import { reactive } from './reactive.js';
    import { effect } from './effect.js'

    const state = reactive({
      name: 'sAnL1ng',
      age: 18
    })

    effect(
      () => {
        console.log(`${state.name}今年${state.age}岁了`);
      },
      { 
        lazy: false
      }
    )
  </script>
</body>
</html>

最后再让我们看一遍效果!

总结

reactive可以将引用类型编程响应式主要依靠于Proxy代理,在对象中的属性被读取的时候需要做副作用函数收集,在对象中的属性被修改的时候需要做副作用函数触发。

假如您也和我一样,在准备春招。欢迎加我微信shunwuyu,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!

开源Git仓库: gitee.com/cheng-bingw...

更多内容:最近手有点痒,手搓一个reactive

相关推荐
new出一个对象3 小时前
uniapp接入BMapGL百度地图
javascript·百度·uni-app
你挚爱的强哥4 小时前
✅✅✅【Vue.js】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本
javascript·vue.js·jquery
y先森5 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy5 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189115 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿6 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡7 小时前
commitlint校验git提交信息
前端
虾球xz7 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇7 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒7 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript