《Vue进阶教程》第二十七课:实现侦听对象

news/2024/12/27 16:50:41/

  往期内容:

《Vue进阶教程》第十六课:深入完善响应式系统之单例模式

《Vue进阶教程》第十七课:支持分支切换

《Vue进阶教程》第十八课:避免死循环

《Vue进阶教程》第十九课:computed初步实现

《Vue进阶教程》第二十课:lazy懒执行

《Vue进阶教程》第二十一课:支持缓存

《Vue进阶教程》第二十二课:自定义更新(调度器)

《Vue进阶教程》第二十三课:渲染计算属性的结果

《Vue进阶教程》第二十四课:优化

《Vue进阶教程》第二十五课:watch基本概念

《Vue进阶教程》第二十六课:实现侦听函数

1) 基本实现

如果第一个参数不是副作用函数, 可以将其包装成一个副作用函数

javascript">function watch(source, cb) {let getterif (typeof source === 'function') {getter = source} else {getter = () => source}effect(getter, {scheduler() {cb()},})
}

由于并没有触发代理对象的取值操作, 因此不会收集依赖

考虑实现一个函数, 遍历访问source中的每一个属性

javascript">function traverse(value) {for (const k in value) {// 将代理对象的每个属性访问一次value[k]}
}
function watch(source, cb) {let getterif (typeof source == 'function') {getter = source} else {getter = () => traverse(source)}effect(getter, {scheduler() {cb()},})
}

2) 支持嵌套

如果代理对象中存在嵌套情况

  1. 在reactive中要递归为嵌套的对象创建代理
  2. 在traverse中要递归访问嵌套的属性

示例

javascript">get(target, key) {if (key == '__v_isReactive') return true // 新增// console.log(`自定义访问${key}`)// 收集依赖track(target, key)if (typeof target[key] == 'object') {// 递归处理对象类型return reactive(target[key])}return target[key]
},
javascript">function traverse(value) {for (const k in value) {if (typeof value[k] == 'object') {traverse(value[k])} else {// 将代理对象的每个属性访问一次value[k]}}
}

3) 解决循环引用问题

如果按上述写法, 会出现递归循环引用, 陷入死循环

什么是循环引用

如果一个对象的某个属性引用自身, 在递归时会死循环

问题示例

javascript">const state = reactive({ name: 'xiaoming', address: { city: '武汉' } })
state.test = state// watch接收响应式对象的情况
watch(state, () => {console.log('该函数默认不执行, 只有状态更新时执行...')
})setTimeout(() => {// 侦听对象时, 是深度侦听state.address.city = '北京'
}, 1000)

以上问题可以简化为

javascript">const obj = {foo: 'foo',
}
// obj的一个属性引用obj本身, 出现循环引用问题
obj.bar = objfunction traverse(value) {for (const k in value) {if (typeof value[k] == 'object') {traverse(value[k])} else {// 将代理对象的每个属性访问一次value[k]}}
}traverse(obj)

为了避免递归循环引用陷入死循环, 改造traverse方法

javascript">function traverse(value, seen = new Set()) {// 如果这个对象已经被遍历过了, 直接返回if (typeof value !== 'object' || seen.has(value)) return// 将遍历的对象记录下来, 再递归时判断seen.add(value)for (let key in value) {// 这里取值, 会触发proxy对象的getter操作traverse(value[key], seen)}
}

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

相关文章

Function.prototype和Object.prototype 的区别

Function.prototype 和 Object.prototype 都是 JavaScript 中的重要原型对象,它们分别用于所有函数对象和所有普通对象的原型链。它们有很多不同之处,主要体现在它们所代表的对象类型和功能上。 1. 作用域和对象类型 Object.prototype 是所有普通 JavaS…

LeetCode:3083. 字符串及其反转中是否存在同一子字符串(哈希 Java)

目录 3083. 字符串及其反转中是否存在同一子字符串 题目描述: 实现代码与解析: 二进制哈希 原理思路: 3083. 字符串及其反转中是否存在同一子字符串 题目描述: 给你一个字符串 s ,请你判断字符串 s 是否存在一个…

计算机网络——期末复习(4)协议或技术汇总、思维导图

思维导图 协议与技术 物理层通信协议:曼彻斯特编码链路层通信协议:CSMA/CD (1)停止-等待协议(属于自动请求重传ARQ协议):确认、否认、重传、超时重传、 (2)回退N帧协…

解决:excel鼠标滚动幅度太大如何调节?

在excel里为什么滚动一次跳过很多行呢?很不方便。。。 1. 问题: 一开始单元格从第1行开始: 鼠标轻轻滚动一下后,直接跳到第4行: 鼠标在word和浏览器里都是好好的。在excel里为什么不是滚动一次跳过一行呢&#xff…

xterm遇到的问题及解决方案

xterm遇到的问题及解决方案 /r插入终端导致的之后插入的数据覆盖了改行头部的数据 问题说明 如图所示,当在一行输入的候,输入的l插入到了改行的头部。 查看ws返回数据 可见ws返回的信息存在\r字符,在xterm.js中\r是回车字符的意思&…

RTMW:实时多人2D和3D 全人体姿态估计

单位:上海AI实验室 代码:mmpose/tree/main/projects/rtmpose 系列文章目录 RTMO: 面向高性能单阶段的实时多人姿态估计 目录 系列文章目录摘要一、背景二、相关工作2.1 自上而下的方法。2.2 坐标分类。2.3 3D Pose 3 实验方法3.1.1 任务限制3.1.3训练技…

利用 Python 编写一个 VIP 音乐下载脚本

在这篇博客中,我们将介绍如何使用 Python 编写一个简单的 VIP 音乐下载脚本,利用网页爬虫技术从一个音乐网站下载歌曲。通过解析网页,获取歌曲的真实下载链接,并将音乐文件保存到本地。我们将使用 requests 和 BeautifulSoup 库来实现这个过程。 目标 本脚本的主要功能是…

裴蜀定理和扩展欧几里得定理

文章目录 裴蜀定理什么是裴蜀定理裴蜀定理证明如下例题思路代码 扩展欧几里得什么是扩展欧几里得定理扩展欧几里得证明如下模板例题思路代码 裴蜀定理 什么是裴蜀定理 若a,b为整数,且gcd(a,b)d,那么对于任…