TS手写简陋版reactive响应式原理(依赖收集,依赖更新)
最近博主看源码了解到了vue3的响应式原理
vue3的响应式实现是依赖收集与依赖更新,vue3已经从Object.property
更换成Proxy
,Proxy
相比于前者可以直接监听对象数组,对于深层次的对象和数组,会把触发对应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
劫持的target
与key
找到对应的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响应式原理(依赖收集,依赖更新)相关推荐
- vue3 ref及reactive响应式原理案例 语法糖/非语法糖
非语法糖: <template> <h1>博主的信息</h1> <h2>姓名:{{name}}</h2> <h2>年龄:{{ag ...
- 深入了解Vue 2响应式原理,并手写一个简单的Vue
1. Vue 2的响应式原理 Vue.js 一个核心思想是数据驱动.所谓数据驱动是指视图是由数据驱动生成的,对视图的修改,不会直接操作 DOM,而是通过修改数据.vue.js里面只需要改变数据,Vue ...
- vue3.0初体验(例子解读reactive响应式)
目录 准备 vue3 reactive原理例子重点讲解 vue3 reactive原理例子完整代码 准备: 下载vue-next 安装依赖 npm install 核心部分package,里面的vue ...
- 玩转Spring—Spring5新特性之Reactive响应式编程实战
1 什么是响应式编程 一句话总结:响应式编程是一种编程范式,通用和专注于数据流和变化的,并且是异步的. 维基百科原文: In computing, reactive programming is an ...
- 手写简单版 Promise
Promise作为ES6新增的函数,帮助我们解决了回调地狱的难题,让我们的异步代码可以更加清晰简洁,作为一名前端程序员,手写简单版Promise应该是必备的技能.接下来不多说,直接上代码了. clas ...
- 手写迷你版 Tomcat - Minicat
大家也可以关注我的公众号,文章也会同步更新,当然,公众号还会有一些资源可以分享给大家~ 手写迷你版 Tomcat - Minicat Minicat 的目标 我们可以通过浏览器客户端发送 http 请 ...
- Vue设计模式,发布订阅,响应式原理(简略版)
Vue mvvm框架是什么? mvvm框架(model-view-viewMode),本质是mvc框架的改进版,mvc框架一旦项目复杂度越来越高,代码量大,维护起来很难,尤其管理层,controlle ...
- 手写简易版链表及原理分析
好多人都觉得为什么要自己写这样的数据结构,变成里面不是有吗?为什么要去写,有这个疑问,其实这个疑问这我的脑海中也存在了很长一段时间,本人是学习java编程的,直接看java的集合框架不行吗?这个时候如 ...
- 解鞍卸甲——手写简易版Spring框架(终):使用三级缓存解决循环依赖问题
什么是三级缓存 按照目前我们实现的 Spring 框架,是可以满足一个基本需求的,但如果你配置了A.B两个Bean对象互相依赖,那么立马会抛出 java.lang.StackOverflowError ...
最新文章
- 开启灯光就是近光吗_摩托车灯光你用对了吗?双闪的作用是什么?
- nginx反向代理相关 负载均衡及优化
- JVM是如何分配和回收内存?有实例!
- centos6.4下LVS+keepalived的高可用(LVS/DR模式)
- [Java开发之路]Java字符串
- R7-2 试试多线程 (10 分)
- 什么可以搜python答案_什么软件可以搜python答案
- nrf52840 gpiote如何配置中断输入_西门子S7-200 SMART PID回路控制,配置PID向导,查看项目组件...
- 云原生应用Go语言:你还在考虑的时候,别人已经应用实践
- 三个月可更改用户昵称两次
- sql integer字置为空_请写一个函数来检查用户提交的数据是否为整数
- 在C++上利用onnxruntime (CUDA)和 opencv 部署模型onnx
- 新塘单片机烧写器_NuMicro ICP Programming Tool-ICP Programming Tool(新唐单片机烧录工具)下载 v3.00.6909官方版--pc6下载站...
- 来,看看记事本里会变成乱码的字……不仅仅是“联通”而已……
- 【钉钉杯大学生大数据挑战赛】初赛 A:银行卡电信诈骗危险预测 Baseline
- JAVA进阶—注解,对象克隆,设计模式
- 淘宝商品信息爬取(已登录)
- 报错:Error: The project seems to require yarn but it‘s not installed解决方案
- 一般情况下的椭圆方程
- 建议收藏:Axure交互常用按钮组