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

关键词

响应系统的工作流程:

  • 读取操作发生时,将副作用函数收集到“桶”中。
  • 设置操作发生时,从“桶”中取出副作用函数并执行。
  • activeEffect 全局变量当做中介存储副作用函数。
  • 重新定义effect函数 =>变成了注册副作用函数的函数

笔记

改善4.2缺陷逻辑

  1. 改善4.2中的缺陷,希望副作用函数的名字改变或者是匿名函数都能正常收集到Set存储桶中。因此需要提供一个注册副作用函数的机制
  2. 副作用函数已存储到activeEffect中,所以get拦截函数应该把activeEffect收集到Set存储桶中,如此响应式系统就不依赖副作用函数的名字了。
// 用一个全局变量存储被注册的副作用函数
let activeEffect
// effect 函数用于注册副作用函数
// 参数fn 就是将要注册的副作用函数
function effect(fn) {// 当调用effect 注册副作用函数时,将副作用函数fn 赋值给actvieEffectactiveEffect = fn// 执行副作用函数fn()
}
// 使用一个匿名的副作用函数作为 effect 函数的参数
// 首先会把匿名的副作用函数 fn 赋值给全局变量 activeEffect
// 接着执行被注册的匿名副作用函数 fn,会触发响应式数据 obj.text 的读取操作,进而触发代理对象 Proxy 的 get 拦截函数
effect(// 一个匿名的副作用函数() => {document.body.innerText = obj.text}
)

代理对象改动

// 代理对象 Proxy
const obj = new Proxy(data, {get(target, key) {// 将 activeEffect 中存储的副作用函数收集到“桶”中if (activeEffect) {  // 新增bucket.add(activeEffect)  // 新增}  // 新增return target[key]},set(target, key, newVal) {target[key] = newValbucket.forEach(fn => fn())return true}
})

修改后存在问题 :不存在属性依然执行

当我们修改响应式数据obj 不存在的一个属性的时候,匿名副作用函数依然会执行

effect(// 一个匿名的副作用函数() => {console.log('effct run') // 会打印两次document.body.innerText = obj.text;}
);setTimeout(()=>{obj.noExist = 'hello vue3'
})

完整代码 demo (如下)

const bucket = new Set();
const data = { text: "hello world" };// 用一个全局变量存储被注册的副作用函数
let activeEffect;
// effect 函数用于注册副作用函数
// 参数fn 就是将要注册的副作用函数
function effect(fn) {// 当调用effect 注册副作用函数时,将副作用函数fn 赋值给actvieEffectactiveEffect = fn;// 执行副作用函数fn();
}// 代理对象 Proxy
const obj = new Proxy(data, {get(target, key) {// 将 activeEffect 中存储的副作用函数收集到“桶”中if (activeEffect) {// 新增bucket.add(activeEffect); // 新增} // 新增return target[key];},set(target, key, newVal) {target[key] = newVal;bucket.forEach((fn) => fn());return true;}
});// 使用一个匿名的副作用函数作为 effect 函数的参数
// 首先会把匿名的副作用函数 fn 赋值给全局变量 activeEffect
// 接着执行被注册的匿名副作用函数 fn,会触发响应式数据 obj.text 的读取操作,进而触发代理对象 Proxy 的 get 拦截函数
effect(// 一个匿名的副作用函数() => {console.log('effct run') // 会打印两次document.body.innerText = obj.text;}
);setTimeout(()=>{obj.noExist = 'hello vue3'
})

根本原因:没有在副作用函数与被操作的目标字段之间建立明确的联系

也就是说,当我们读取或者设置时,无论操作哪一个属性,那么get和set方法都会执行

解决方案: 修改数据结构

观察代码,并思考如何设计数据结构。(树型结构)

effect(function effectFn() {document.body.innerText = obj.text
})

在这段代码中存在三个角色:

  • 被操作(读取)的代理对象 obj
  • 被操作(读取)的字段名 text
  • 使用 effect 函数注册的副作用函数 effectFn

树形结构(三种情况举例)

使用WeakMap 代替Set作为桶的数据结构

// 存储副作用函数的桶
const bucket = new WeakMap()

修改get/set 拦截器

const obj = new Proxy(data,{get(target,key){// 没有activeEffect直接returnif(!activeEffect){return target[key]}// 根据target 从WeakMap桶中取得depsMap,它也是一个Map类型:key-->effectslet depsMap = bucket.get(target)// 如果不存在depsMap 则新建一个Map与target关联if(!depsMap) {bucket.set(target,(depsMap = new Map()))}// 根据key 从 depsMap 中取得deps,它是一个Set类型// 里面存储这所有与当前Key相关联的副作用函数effectslet deps = depsMap.get(key)// 如果deps不存在 同样新建一个Set并关联if(!deps) {depsMap.set(key,(deps = new Set()))}// 最后将当前激活的副作用函数添加到WeakMap 桶里deps.add(activeEffect)// 返回属性值return target[key]},set(target,key,newVal){// 设置属性值target[key] = newVal// 根据target 从WeakMap桶中取得depsMap 它是key -->effectsconst depsMap = bucket.get(target)if(!depsMap) { return }//根据key 获取所有副作用函数effectsconst effects = depsMap.get(key)// 执行副作用函数effects && effects.forEach(fn=>fn())}
})

WeakMap、Map 和 Set 之间的关系

为什么要使用 WeakMap(WeakMap与Map的区别)

WeakMap 对 key 是弱引用,不影响垃圾回收机制,不会造成内存泄露,自动回收。

立即执行函数执行完毕后,Map的key foo 依然保持着对 foo 对象的引用,而WeakMap 的key bar 属性就不能访问了。

更多参见底部延伸阅读

const map = new Map();
const weakmap = new WeakMap();
(function(){const foo = {foo: 1};const bar = {bar: 2};map.set(foo, 1);weakmap.set(bar, 2);
})()

优化代码 封装内部逻辑 trigger 触发 track 追踪

代码优化,封装get和set内把副作用函数放入桶内和执行副作用函数的逻辑

const obj = new Proxy(data, {// 拦截读取操作get(target, key) {// 将副作用函数 activeEffect 添加到存储副作用函数的桶中track(target, key)// 返回属性值return target[key]},// 拦截设置操作set(target, key, newVal) {// 设置属性值target[key] = newVal// 把副作用函数从桶里取出并执行trigger(target, key)}
})
// 在 get 拦截函数内调用 track 函数追踪变化
function track(target, key) {// 没有 activeEffect,直接 returnif (!activeEffect) returnlet depsMap = bucket.get(target)if (!depsMap) {bucket.set(target, (depsMap = new Map()))}let deps = depsMap.get(key)if (!deps) {depsMap.set(key, (deps = new Set()))}deps.add(activeEffect)
}
// 在 set 拦截函数内调用 trigger 函数触发变化
function trigger(target, key) {const depsMap = bucket.get(target)if (!depsMap) returnconst effects = depsMap.get(key)effects && effects.forEach(fn => fn())
}

延伸阅读

阮一峰 Es6 WeakMap

张鑫旭 JS WeakMap应该什么时候使用

4.3 设计一个完善的响应式系统相关推荐

  1. (6K字!)从零实现vue3响应式系统!

    在介绍响应式系统之前,先了解一些概念. 副作用函数 能产生副作用的函数就是副作用函数. 何为副作用?在我理解,就是会可能对其他的除了自身以外的数据造成变化的的函数.比如修改了全局变量,修改了引用的参数 ...

  2. 深入剖析Vue源码 - 响应式系统构建(上)

    从这一小节开始,正式进入Vue源码的核心,也是难点之一,响应式系统的构建.这一节将作为分析响应式构建过程源码的入门,主要分为两大块,第一块是针对响应式数据props,methods,data,comp ...

  3. 经典网页设计:10个响应式设计的国外购物网站

    今天我想与大家分享电子商务主题的网站设计,更精确地说是为设计在线商店提供新思想.每个人都知道移动技术的市场发展迅速,已经很难找到一个人没有手机的人了.响应设计给我们提供了一个巨大的机遇:站点的访问量的 ...

  4. 响应式网页设计代码_消除响应式网站建设设计中的缺陷

    在过去的5年里,移动流量的份额增长了20%,现在响应式网站设计已经被认为是理所当然的了.到2020年,您可以通过手机几乎可以访问任何网站,它会很好用.大多数用户如果看到一个网站在他们的智能手机或平板电 ...

  5. UI设计中如何做响应式设计与自适应设计

    UI设计中如何做响应式设计与自适应设计?由于科技在不断的发展,小伙伴们上网就不单单只依靠台式电脑了,还有平板电脑笔记本电脑都是可供大家选择的.面对不同的屏幕分辨率网站是如何进行适配的呢?今天AAA教育 ...

  6. HTML5期末大作业:旅游网站设计——简单大气的响应式旅游网页(5页) HTML+CSS+JavaScript...

    HTML5期末大作业:旅游网站设计--简单大气的响应式旅游网页(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机毕设网页设计源码 常见网页 ...

  7. html网页设计期末大作业——响应式化妆品护肤品网页(11页) web课程设计 网页规划与设计

    html网页设计期末大作业--响应式化妆品护肤品网页(11页) web课程设计 网页规划与设计 常见网页设计作业题材有 个人. 美食. 公司. 学校. 旅游. 电商. 宠物. 电器. 茶叶. 家居. ...

  8. HTML5期末大作业:电竞游戏网站设计——电竞游戏介绍响应式网页(7页) HTML+CSS+JavaScript

    HTML5期末大作业:电竞游戏网站设计--电竞游戏介绍响应式网页(7页) HTML+CSS+JavaScript 常见网页设计作业题材有 个人. 美食. 公司. 学校. 旅游. 电商. 宠物. 电器. ...

  9. 意外的服务器响应_响应式系统reactive system初探

    初识响应式系统 第一次听到reactive这个词还是在几年前,偶然了解到了Rxjava这个项目,仿佛为我打开了一扇新的大门,Rxjava是ReactiveX的java实现,ReactiveX家族除了R ...

最新文章

  1. SQL:安装多个实例,修改实例端口号,和IP加端口号连接实例
  2. httpsendrequest的head怎么用string写_商品广告语用怎么写?男人篇
  3. boost::range模块实现map_values相关的测试程序
  4. jvm虚拟机_一文入门jvm虚拟机
  5. lua transliterate实现(lua程序设计10.6练习10.3题)
  6. callback的实现
  7. 深入了解DSP和ARM的关系(相同与区别)
  8. 原生ajax请求超时处理,关于原生AJAX
  9. 抚琴成一快-电吉他内录(Zoom G3为例)
  10. Apollo canbus模块学习笔记
  11. CentOS 7.5 安装Nginx教程
  12. 005-浅谈SSDT
  13. 服务器电脑用哪个系统好,电脑系统哪个好用?电脑系统有几种版本
  14. 【赠书福利】人工智能发展的三驾马车
  15. 计算机初中毕业好学吗,初中毕业学计算机好学吗?
  16. java中批量下载图片(httpClient)
  17. QT Desinger设计程序主界面 PyUIC生成.py
  18. Windows10 64位 + caffe + Matlab -- cpu版本
  19. Linux Deepin 新增英国肯特大学镜像服务
  20. 数据挖掘(pandasxgboost)

热门文章

  1. OEA框架学习:元数据设计
  2. python自动排课表_利用python爬取广西科技大学教务管理信息系统班级课表
  3. CSS Counters
  4. autohotkey 常见语法
  5. labview循环-移位寄存器妙用
  6. 灌溉系统通过远程io模块控制多个喷淋阀门开关
  7. VLC播放器应用------LIBVLC API解析
  8. 云端渲染时长1.58亿核小时,阿里云助力国漫巨制《新神榜:杨戬》提升视效
  9. 08_短信大全_完成
  10. java提高篇(三)-----理解java的三大特性之多态