最近博主看源码了解到了vue3的响应式原理

vue3的响应式实现是依赖收集与依赖更新,vue3已经从Object.property更换成ProxyProxy相比于前者可以直接监听对象数组,对于深层次的对象和数组,会把触发对应getter,然后去递归进行依赖收集,并不是直接像vue2暴力那样递归,总体而言性能更好

实现原理:

  • 通过Proxy(代理):拦截对象中任意属性的变化,包括属性的读写,属性的添加,属性的删除等。
  • 通过Reflect(反射):对源对象的属性进行操作

接下来博主带你们手写一波简单的源码操作

  • 对reactive传进来的对象进行Proxy进行劫持在内部进行依赖收集与通知更新操作
import { track,trigger } from "./effect"//判断是否是对象的
const isObject = (target) => target!=null && typeof target == 'object'export const reactive = <T extends object>(target:T) =>{return new Proxy(target,{//返回三个参数/**** @param target 传入的当前对象* @param key 传入的当前属性* @param receiver 其实也是当前对象*/get(target,key,receiver){// return target[key] //某些情况特定情况会上下文错乱//解决上下文错乱//Reflect ES新增 对象取值,接收三个参数let res = Reflect.get(target,key,receiver) as object //断言成object//依赖收集track(target,key)//深层次的递归if(isObject(res)){return reactive(res)}return res},//需要返回一个布尔值set(target,key,value,receiver){//Reflect.set正好返回一个布尔值let res = Reflect.set(target,key,value,receiver)//依赖更新trigger(target,key)return res}})
}

采用Reflet对对象进行标准化操作,因为如果直接采用JS如果失败了,不会产生异常提示

这样在进行获取数据是后进行依赖收集,在更新数据后进行通知依赖更新

补充知识点

1.Reflect.get()

Reflect.get(target, propertyKey[, receiver])
用于从对象中获取属性的值

target: 目标对象
propertyKey: 需要获取值的属性名称
receiver: 如果遇到getter,此值将提供给目标调用

2.Reflect.set()

Reflect.set(target, propertyKey, value[, receiver])

在对象上设置属性,返回一个Bool值来表示是否成功设置属性。

target: 目标对象
propertyKey: 设置的属性名称
value: 设置的属性值
receiver: 如果遇到setter, this将提供给目标调用

依赖收集

//依赖收集
/*** * @param target 接收这个对象* @param key *///需要一个全局变量把它收集起来
//WeakMap只接收object的类型
//target正好是一个对象
const targetMap = new WeakMap()
export const track = (target,key) =>{let firstDeepMap = targetMap.get(target)//第一层数据结构//第一次是没有这个值的if(!firstDeepMap){//所以给它填充一下firstDeepMap = new Map()//通过targetMap 给它添加进去targetMap.set(target,firstDeepMap)}//第二层数据结构let secondDeepMap = firstDeepMap.get(key) //通过这个key 去取第二层的new Set//第一次没有值 取不到 需要填充if(!secondDeepMap){secondDeepMap = new Set()firstDeepMap.set(key,secondDeepMap)}//第三层把它关联起来(effect,effect...)secondDeepMap.add(activeEffect)
}

我们首先new了一个weakMap,weakMap只接受object的类型,target正好是一个对象,然后我们通过target获取到对应的内部Map,我们在通过key获取到Set的集合,此时内部存储的就是一个个所收集到的依赖

这里使用WeakMap原因是它是一个弱引用,不会影响垃圾回收机制回收。

activeEffect是什么呢

effect可以接收一个匿名的函数 客户可以自己去自定义

匿名函数我们每次把它收集起来 然后依赖发生变化时  去执行这个里面副作用函数,实现依赖收集和依赖更新

我们自己定义了一个简陋的版本,闭包收集去执行它

//effect可以接收一个匿名的函数 客户可以自己去自定义
//匿名函数我们每次把它收集起来 然后依赖发生变化时  去执行这个里面副作用函数,实现依赖收集和依赖更新//定义一个全局变量 把这个闭包给收集起来
//简陋版本
let activeEffect;
export const effect = (fn:Function) =>{//闭包const _effect = function(){activeEffect = _effect //收集起来执行一下fn()}//首次默认给它调用一下_effect()
}

我们简单的可以就把他理解成一个依赖,用户使用了effect函数过后,里面的响应式数据发生变化后会重新执行传递进去的回调函数,vue2中收集的依赖对应watcher,vue3收集的依赖实际是effect,他们两者实现功能实际上是一样的。

依赖更新

这里暂不考虑DOM问题,操作起来其实很简单就是通过被Proxy劫持的targetkey找到对应的Set集合调用用户传递的effect函数进行依赖更新

//依赖更新
//通过全局变量targetMap取到firstDeepMap
export const trigger = (target,key) =>{const firstDeepMap = targetMap.get(target)const secondDeepMap = firstDeepMap.get(key)//收集到的就是一个副作用函数effect,进行更新secondDeepMap.forEach(effect => effect())
}

则全部代码整理如下effect.ts


//effect可以接收一个匿名的函数 客户可以自己去自定义
//匿名函数我们每次把它收集起来 然后依赖发生变化时  去执行这个里面副作用函数,实现依赖收集和依赖更新//定义一个全局变量 把这个闭包给收集起来
//简陋版本
let activeEffect;
export const effect = (fn:Function) =>{//闭包const _effect = function(){activeEffect = _effect //收集起来执行一下fn()}//首次默认给它调用一下_effect()
}//依赖收集
/*** * @param target 接收这个对象* @param key *///需要一个全局变量把它收集起来
//WeakMap只接收object的类型
//target正好是一个对象
const targetMap = new WeakMap()
export const track = (target,key) =>{let firstDeepMap = targetMap.get(target)//第一层数据结构//第一次是没有这个值的if(!firstDeepMap){//所以给它填充一下firstDeepMap = new Map()//通过targetMap 给它添加进去targetMap.set(target,firstDeepMap)}//第二层数据结构let secondDeepMap = firstDeepMap.get(key) //通过这个key 去取第二层的new Set//第一次没有值 取不到 需要填充if(!secondDeepMap){secondDeepMap = new Set()firstDeepMap.set(key,secondDeepMap)}//第三层把它关联起来(effect,effect...)secondDeepMap.add(activeEffect)
}//依赖更新
//通过全局变量targetMap取到firstDeepMap
export const trigger = (target,key) =>{const firstDeepMap = targetMap.get(target)const secondDeepMap = firstDeepMap.get(key)//收集到的就是一个副作用函数effect,进行更新secondDeepMap.forEach(effect => effect())
}

最后index.html可以执行

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><div id="app"></div><script type="module">import { reactive } from "./reactive.js";import { effect } from "./effect.js";const user = reactive({name: "xiaochen",age: 18,foo: {bar: {sss: 123,},},});effect(() => {document.querySelector("#app").innerText = `${user.name} - ${user.age}-${user.foo.bar.sss}`;});setTimeout(() => {user.name = "dachen";setTimeout(() => {user.age = "23";setTimeout(() => {user.foo.bar.sss = 66666666;}, 1000);}, 1000);}, 2000);</script></body>
</html>

TS手写简陋版reactive响应式原理(依赖收集,依赖更新)相关推荐

  1. vue3 ref及reactive响应式原理案例 语法糖/非语法糖

    非语法糖: <template> <h1>博主的信息</h1> <h2>姓名:{{name}}</h2> <h2>年龄:{{ag ...

  2. 深入了解Vue 2响应式原理,并手写一个简单的Vue

    1. Vue 2的响应式原理 Vue.js 一个核心思想是数据驱动.所谓数据驱动是指视图是由数据驱动生成的,对视图的修改,不会直接操作 DOM,而是通过修改数据.vue.js里面只需要改变数据,Vue ...

  3. vue3.0初体验(例子解读reactive响应式)

    目录 准备 vue3 reactive原理例子重点讲解 vue3 reactive原理例子完整代码 准备: 下载vue-next 安装依赖 npm install 核心部分package,里面的vue ...

  4. 玩转Spring—Spring5新特性之Reactive响应式编程实战

    1 什么是响应式编程 一句话总结:响应式编程是一种编程范式,通用和专注于数据流和变化的,并且是异步的. 维基百科原文: In computing, reactive programming is an ...

  5. 手写简单版 Promise

    Promise作为ES6新增的函数,帮助我们解决了回调地狱的难题,让我们的异步代码可以更加清晰简洁,作为一名前端程序员,手写简单版Promise应该是必备的技能.接下来不多说,直接上代码了. clas ...

  6. 手写迷你版 Tomcat - Minicat

    大家也可以关注我的公众号,文章也会同步更新,当然,公众号还会有一些资源可以分享给大家~ 手写迷你版 Tomcat - Minicat Minicat 的目标 我们可以通过浏览器客户端发送 http 请 ...

  7. Vue设计模式,发布订阅,响应式原理(简略版)

    Vue mvvm框架是什么? mvvm框架(model-view-viewMode),本质是mvc框架的改进版,mvc框架一旦项目复杂度越来越高,代码量大,维护起来很难,尤其管理层,controlle ...

  8. 手写简易版链表及原理分析

    好多人都觉得为什么要自己写这样的数据结构,变成里面不是有吗?为什么要去写,有这个疑问,其实这个疑问这我的脑海中也存在了很长一段时间,本人是学习java编程的,直接看java的集合框架不行吗?这个时候如 ...

  9. 解鞍卸甲——手写简易版Spring框架(终):使用三级缓存解决循环依赖问题

    什么是三级缓存 按照目前我们实现的 Spring 框架,是可以满足一个基本需求的,但如果你配置了A.B两个Bean对象互相依赖,那么立马会抛出 java.lang.StackOverflowError ...

最新文章

  1. 开启灯光就是近光吗_摩托车灯光你用对了吗?双闪的作用是什么?
  2. nginx反向代理相关 负载均衡及优化
  3. JVM是如何分配和回收内存?有实例!
  4. centos6.4下LVS+keepalived的高可用(LVS/DR模式)
  5. [Java开发之路]Java字符串
  6. R7-2 试试多线程 (10 分)
  7. 什么可以搜python答案_什么软件可以搜python答案
  8. nrf52840 gpiote如何配置中断输入_西门子S7-200 SMART PID回路控制,配置PID向导,查看项目组件...
  9. 云原生应用Go语言:你还在考虑的时候,别人已经应用实践
  10. 三个月可更改用户昵称两次
  11. sql integer字置为空_请写一个函数来检查用户提交的数据是否为整数
  12. 在C++上利用onnxruntime (CUDA)和 opencv 部署模型onnx
  13. 新塘单片机烧写器_NuMicro ICP Programming Tool-ICP Programming Tool(新唐单片机烧录工具)下载 v3.00.6909官方版--pc6下载站...
  14. 来,看看记事本里会变成乱码的字……不仅仅是“联通”而已……
  15. 【钉钉杯大学生大数据挑战赛】初赛 A:银行卡电信诈骗危险预测 Baseline
  16. JAVA进阶—注解,对象克隆,设计模式
  17. 淘宝商品信息爬取(已登录)
  18. 报错:Error: The project seems to require yarn but it‘s not installed解决方案
  19. 一般情况下的椭圆方程
  20. 建议收藏:Axure交互常用按钮组

热门文章

  1. 飞桨PaddleOCR C++预测库布署
  2. python进程实现生产者消费者模型
  3. prepend 和 prependTo的区别
  4. Ubuntu下的Nginx-Uwsgi-Django项目部署详细流程
  5. 认真面对生活,认真的享受生活!
  6. fatfs文件系统与u盘驱动联立起来(usb_host)
  7. Jupyter_notebook_Jupyter快捷键总结
  8. HTML实现3D相册
  9. Tomcat详细介绍和使用总结
  10. java 判断是否中文字符_java中判断字符串是否是中文的方法