vue.js的设计与实现(响应系统1)

news/2024/10/18 14:39:25/

文章目录

    • 概要
    • 响应式数据与副作用函数
    • 响应式数据的基本实现
    • 设计一个完善的响应式系统
    • 小结

概要

响应系统式vue的重要组成部分,我们都知道vue3中采用了proxy实现响应式数据的,那是怎么实现的呢?我们往下看

响应式数据与副作用函数

大家肯定会问:什么是副作用函数呢?如以下代码:

function effect(){document.body.innerText = 'hello vue3'
}

当effect函数执行时,他会设置body的文本内容,但除了effect函数之外的任何函数都可能会读取或设置body的文本内容。也就是说effect函数的执行会直接或者间接影响到其他函数的执行,这时我们就会说effect函数产生了副作用。

理解了副作用函数,我们再说说什么是响应式数据,看一下代码

const obj = {text:'hello word'}
function effect(){document.body.innerText = obj.text
}
obj.text = 'hello vue3'

看以上代码,effect中设置body的文本内容是使用了obj中text的值,当我们去改变text值的时候,我们会希望副作用effect函数要重新执行,但是很明显,以上代码无法做到这点。

响应式数据的基本实现

我们要怎么让obj变成响应式数据呢?想到这里,大家都会说用proxy把!没错就是用proxy,那具体要怎么做呢?

我们知道了改变obj的text值的时候,需要重新执行effect函数。我们可以观察到 effect函数中 有读取obj.text,这就会出发proxy的get操作,当哦我们修改obj.text值时,就会触发set操作。当然,我们可能不仅仅只有一个effect这个副作用函数,可能会有多个,这个时候我们会有什么办法呢?对的,我们可以把出发get操作的所有副作用函数存起来,在触发set操作时,拿出来全部执行一遍,那我们就会想到一下的写法:

const bucket = new Set()
const data = {text:'hello word'}
const obj = new Proxy(data,{get(target,key){bucket.add(effect)return target[key]},set(target,key,newVal){target[key] = newValbucket.forEach(fn=>fn())return true}
})

这里为什么需要用Set呢?就是为了使用Set的特性去重,因为我们每次执行effect的时候 都会执行一次get方法,如果我们使用正常Array来存储的话,那是不是会执行两次effect副作用函数了呢?
这样 我们在执行一次以下代码,看下是不是我们想要的效果

function effect (){document.body.innerText = obj.text
}
effect()
setTimeout(()=>{obj.text = 'hello vue3'
},1000)

我们会发现 当改变obj.text时,又执行了一次effect副作用函数,把body的文本内容更改了

设计一个完善的响应式系统

通过上面,我们以及实现了一个简单的响应式了,我们会发现直接通过名字effect来获取副作用函数,这种编码的方式不够灵活。副作用函数的名字我们应该是要可以随便取的,

所以我们要对以上的代码进行一个改造,我们可以把effect函数赋值给一个变量,这样我们就不会限制副作用函数的命名了。如以下代码:

//这里我们声明一个全局变量来存储被注册的副作用函数
let activeEffect
function effect(fn){activeEffect = fnfn()
}
//effect函数的使用方法就会变成传入一个函数为参数
effect(()=>{document.body.innerText = obj.text
})
//我们在注册proxy代理的时候 就应该把effect变成activeEffect
const bucket = new Set()
const data = {text:'hello word'}
const obj = new Proxy(data,{get(target,key){if(activeEffect){ //这里需要判断一下是否存在副作用函数,存在就加进去,不存在就不需要bucket.add(activeEffect)}return target[key]},set(target,key,newVal){target[key] = newValbucket.forEach(fn=>fn())return true}
})

你觉得这个就是vue3响应式的原理的吗?不远远没有这么简单,我们可以随便测试一下,如果我们添加一个obj里面不存在的属性,也会执行这个副作用函数,因为我们的副作用函数没有和具体的key做绑定,只要有执行set方法,就会执行副作用函数,即使没有读取改变的属性。我们想要的对应关系应该如下:

target->key->effectFn
我们只有改变对应的key值才会执行对应key的副作用函数,那我们就对这个 bucket 存储副作用函数的地方进行改造,如一下代码:

const bucket = new WeakMap()
const data = {text:'hello word'}
const Obj = new Proxy(data,{get(target,key){if(!activeEffect){return target[key]}const desMap =  bucket.get(target)if(!desMap){bucket.set(target,desMap=new Map())}const deps = desMap.get(key)if(!deps){desMap.set(key,deps = new Set())}deps.add(activeEffect)return target[key]},set(target,key,newVal){target[key] = newValconst depsMap = bucket.get(target)if(!depsMap ) return const effect = depsMap.get(key)effect && effect.forEach(fn=>fn())return true}
})

这里可能就有同学要问了,为什么这里要使用weakMap呢?因为:weakMap是弱引用,当它的key值不在被调用的的时候,会被垃圾回收机制回收掉,举一个很简单的例子

const weakmap = new WeakMap()
(function (){const foo ={foo:1}weakmap.set(foo,1)
})()

当我们执行完这个代码后,foo会立刻被垃圾回收机制回收掉,因为weakmap是一个弱引用,不会影响垃圾回收机制回收垃圾,当foo从内存中移除,我们便无法weakmap的key值,也就无法通过weakmap获取对象foo。

我们再把上面的代码稍微优化一下,做下封装处理,能够更好的复用:

const Obj = new Proxy(data,{get(target,key){track(target,key)return target[key]},set(target,key,newVal){target[key] = newValtrigger(target,key)}
})
function track(target,key) {if(!activeEffect) return const desMap =  bucket.get(target)if(!desMap){bucket.set(target,desMap=new Map())}const deps = desMap.get(key)if(!deps){desMap.set(key,deps = new Set())}deps.add(activeEffect)
}
function trigger(target,key){const depsMap = bucket.get(target)if(!depsMap ) return const effects = depsMap.get(key)effects && effects.forEach(fn=>fn())
}

小结

以上就是基础的依赖收集,当然还是会有问题的,下一篇文章见,有错误的地方,希望给位同学可以指出,相互探讨!


http://www.ppmy.cn/news/1514239.html

相关文章

gazebo下使用Fast-planner配置(包含mpc局部规划+控制Gazebo小车以及FastPlanner配置)

源码链接: https://github.com/USE-jx/NMPC_CASADI_CPP?tabreadme-ov-file #这是NMPC的 里面有Fast-Planner,但编译可能缺少东西,所以再放一个Fast-Planner的,可以装装缺少的库 https://github.com/HKUST-Aerial-Robotics/Fast-P…

YOLOv10:面向下一代目标检测模型的创新探索

随着计算机视觉技术的飞速发展,目标检测模型在各类应用场景中的重要性与日俱增。从自动驾驶到智能监控,目标检测的准确性和实时性都直接影响着应用的效果和用户体验。YOLO(You Only Look Once)系列作为实时目标检测的代表性模型&a…

Idea里配置Maven版本

一、安装Maven 1. 官网下载maven地址: Maven – Download Apache Maven Binary是可执行版本,已经编译好可以直接使用。 Source是源代码版本,需要自己编译成可执行软件才可使用。tar.gz和zip两种压缩格式,其实这两个压缩文件里面包含的内容是…

Google Play开发者账号地址验证难题?这些经验或许能帮到你

目前,想要把应用顺利上架到 Google Play,已经不像以前那么简单了,主要是开发者需要应对 Google 日益严格的审核机制。其中,账号验证的地址验证绝对是让很多人头疼的一个环节。 今天就来给大家分享一些真实的经验和干货&#xff0c…

解决ubuntu22.04无法识别CH340/CH341和vscode espidf插件无法选择串口设备节点问题

文章目录 解决ubuntu22.04无法识别CH340/CH341和vscode espidf插件无法选择串口设备节点问题不识别CH340/CH341报错解决办法升级驱动编译安装 卸载brltty程序 vscode espidf插件无法选择串口设备节点问题解决办法编译安装 解决ubuntu22.04无法识别CH340/CH341和vscode espidf插…

短视频SDK解决方案,高效集成,助力商业变现

美摄科技,作为业界领先的多媒体技术服务商,其全面升级的短视频SDK解决方案,旨在为开发者与内容创作者提供一站式、高效能的创作工具,让每一个灵感都能瞬间转化为触动人心的视频作品。 【一站式解决方案,重塑短视频创作…

Qt+ffmpeg环境搭建

Qtffmpeg环境搭建 各平台常见视频开发库举例: iOS:AVFoundation AudioUnitAndroid:MediaPlayer,MediaCodecWindows:DirectShowLinux:GStreamer FFmpeg 库是一个跨平台的视频开发库, 还有 libVLC 也是一个跨平台的视频开…

【速览】数据库-MySQL(更新中)

目录 一、背景二、优缺点三、适用场景四、核心组成基本语法数据库引擎事务索引锁连接池分库分表主从读写分离备份 五、底层原理六、对比参考 一、背景 这个技术出现的背景、初衷和要达到什么样的目标或是要解决什么样的问题。这个问题非常关键,也就是说,…