手写 mini-Vue3的响应式 reactive 下

2,297 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

手写 mini-Vue3的响应式 reactive 下

书接上一回手写 mini-Vue3的响应式 reactive,接下来我们来实现响应式原理的依赖收集,我们分别来实现 effect && track

effect

1. 初始化

这里我们先把effect函数进行定义,并且接收一个参数 callback,还需要执行一次该函数

export function effect(callback) {
  callback()
}

这里执行 callback 是为了 访问响应式对象属性,去收集依赖

2. 存储callback

在收集依赖的过程中,我们需要将 callback 存储起来

let activeEffect = null 
export function effect(callback) {
  activeEffect = callback // 访问响应式对象属性,去收集依赖 
  callback()
  activeEffect = null
}
  • 为什么还需要重新设置activeEffect = null
  • 因为收集依赖的时候,如果有嵌套属性的话,是一个递归的过程

track

1.初始化

track函数接收两个参数

  • 目标对象 traget
  • 要跟踪的属性 key
export function track(target, key) {}

2. 存储target

这里我们需要将 target 存储到 targetMap 中

let targetMap = new WeakMap()

export function track(target, key) {
  if(!activeEffect) return
  let depsMap = targetMap.get(target)
  if(!depsMap) {
    targetMap.set(target, (depsMap = new Map()))
  }
  let dep = depsMap.get(key)
  if(!dep) {
    depsMap.set(key, (dep = new Set()))
  }
  dep.add(activeEffect)
}

收集依赖

这里我们只需要找到我们之前编写的 reactive 函数中的 get 加入如下的模块就好

...
 get(target, key, receiver) {
  // 收集依赖
  track(target, key)
},
...

trigger函数

1.初始化

trigger函数接收两个参数

  • traget
  • key
export function trigger(target, key) {
}

2.我们需要根据 target 从 targetMap 中 找到 depsMap

export function trigger(target, key) {
  // 我们需要根据 target  从 targetMap 中 找到 depsMap
  const depsMap = targetMap.get(target)
  if(!depsMap) return
  // 根据key找对应的 dep集合
  const dep = depsMap.get(key)
  // dep集合中存储的是 对应key存储的 effect 函数
  if(dep){
    dep.forEach(effect => {
      effect()
    });
  }
}

3. 修改触发更新函数

set(target, key, value, receiver) {
    trigger(target, key)
},

ok,这里就完成的差不多了,对了这里推荐一个测试工具LiveServer

测试

<!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, effect } from './reactivity/index.js'

    const product = reactive({
      name: 'iPhone',
      price: 5000,
      count: 3
    })
    let total = 0 
    effect(() => {
      total = product.price * product.count
    })
    console.log(total)

    product.price = 4000
    console.log(total)

    product.count = 1
    console.log(total)

  </script>
</body>
</html>

预期:

  • 15000
  • 12000
  • 4000

image.png 没有问题!