硬核Vue3响应式原理解析,为你保驾护航渡过寒冬

前言

大家好,我是落叶小小少年,虽然比较菜,虽然才开始写作分享,我始终相信

  • 核心demo更容易理解深的技术点
  • 每一次基础的学习都是对知识的巩固

因为从年初就开始使用Vue3了,现在才来学习Vue3,但是也不算晚,学到就是赚到,知识无价,只要今天的知识比昨天多一点就是在丰富自己。那么我们就来学习下Vue3的响应式原理

Vue3的响应式原理

大家都知道Vue3使用的是Proxy进行代理的,这里我们先用Proxy实现一个最基础的响应式

我知道看vue3源码是一件比较头疼的事,所以在这里给大家提取出最简单的一个响应式demo,方便大家学习和理解

1. 响应式函数

先写一个创建reactive的函数,里面使用了Proxy进行target对象的代理

function createReactiveObject(target, baseHandler) {
  const proxy = new Proxy(target, baseHandler);
  return proxy;
}

2. Proxy的handler函数

接着我们实现对应的baseHandler函数,其中最主要的是get和set(当然还有has、ownKeys等就先不实现)

function get(target, key, recivier) {
  const res = Reflect.get(target, key, recivier);
  track(target, key);
  // 深层响应式
  return res !== null && typeof res === 'object' ? createReactiveObject(res) : res;
}
​
function set(target, key, value, recivier) {
  const oldValue = target[key];
  const res = Reflect.set(target, key, value, recivier);
  if (!Object.is(value, oldValue)) {
    trigger(target, key);
  }
  return res;
}

对应的get和set函数做的最主要的事情就是追踪和触发

追踪就是指当你去获取这个target上的key值时,能追踪到你使用了这个target上的key值,如果放到target对象来说,就是指你调用了target[key],比如console.log(target[key]),这就是最简单的访问

触发就是指当你去修改这个值时,你已经追踪了这个值,那么你会有一个bucket去存这个副作用函数,当你修改对应key的值的时候,就需要从这个bucket里面找到对应的key的副作用函数,再去执行,达到响应式的效果

3. 如何设计副作用函数收集数据结构

那么我们应该怎么设计这个bucket才能正确存储上这个effect呢?

我们已经知道访问target[key]时要进行track,可以看出来和target和key有关系,但是一个target[key]肯定不止一个副作用函数使用,当然一个target上也有多个key可以收集副作用函数,所以我们需要这么设计:

targetMap是一个target对象到Map的映射,一个target上有多个key,所以key可以用Map存储

keyToDepMap是每个key对应的dep的映射,一个key可以对应多个dep(副作用函数收集)

在这里插入图片描述

4. 收集和触发

收集主要在track函数里面,触发则主要在trigger函数里面,也就是按照我们上面的结构在get时进行副作用收集和set时取出对应的副作用函数触发即可

const targetMap = new WeakMap();
function track(target, key) {
  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(effect);
}
​
function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  const dep = depsMap.get(key);
  if (dep) {
    // 副作用函数触发
    dep.forEach((fn) => fn());
  }
}

最简单的方法就是直接写一个effect函数吧

上面看到我们实现了track和trigger,那么副作用函数从哪来呢?

const data = {
  name: 'reactive'![在这里插入图片描述](https://img-blog.csdnimg.cn/9335d91de11b4df49f02de84e522c8a8.png)


}
const proxyData = createReactiveObject(data, {
  get,
  set,
});
function effect () {
  // 直接访问了proxyData上的name属性
  console.log(proxyData.name); // reactive
}
effect()
// 修改后会触发trigger里面收集到的函数
proxyData.name = 'ref' // ref

正如我们所预料的,当修改了name值后,重新触发了effect函数,也就是从targetMap中找到了对应的effect函数,重新输出了ref值。targetMap的值如下:

在这里插入图片描述

至此已经实现了一个最小最简单的响应式原理。

在这里插入图片描述

5. 解决effect名字写死问题

大家还记得上面我们在track函数里面收集effect函数时吗?

dep.add(effect)这个add是固定添加effect函数的,这里就会有两个问题

  • 只能使用effect这个副作用函数名
  • 如果多个字段的副作用函数没办法区分开来

那么我们怎么解决这个问题呢?

很简单,就是用一个专门的name来收集effect函数,比如activeEffect,在执行某个副作用函数时就将activeEffect赋值乘这个副作用函数就行了

// 接上
let activeEffect
function effect(fn
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值