4.3 设计一个完善的响应式系统
4.3 设计一个完善的响应式系统
关键词
响应系统的工作流程:
- 当读取操作发生时,将副作用函数收集到“桶”中。
- 当设置操作发生时,从“桶”中取出副作用函数并执行。
- activeEffect 全局变量当做中介存储副作用函数。
- 重新定义effect函数 =>变成了注册副作用函数的函数。
笔记
改善4.2缺陷逻辑
- 改善4.2中的缺陷,希望副作用函数的名字改变或者是匿名函数都能正常收集到Set存储桶中。因此需要提供一个注册副作用函数的机制。
- 副作用函数已存储到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 设计一个完善的响应式系统相关推荐
- (6K字!)从零实现vue3响应式系统!
在介绍响应式系统之前,先了解一些概念. 副作用函数 能产生副作用的函数就是副作用函数. 何为副作用?在我理解,就是会可能对其他的除了自身以外的数据造成变化的的函数.比如修改了全局变量,修改了引用的参数 ...
- 深入剖析Vue源码 - 响应式系统构建(上)
从这一小节开始,正式进入Vue源码的核心,也是难点之一,响应式系统的构建.这一节将作为分析响应式构建过程源码的入门,主要分为两大块,第一块是针对响应式数据props,methods,data,comp ...
- 经典网页设计:10个响应式设计的国外购物网站
今天我想与大家分享电子商务主题的网站设计,更精确地说是为设计在线商店提供新思想.每个人都知道移动技术的市场发展迅速,已经很难找到一个人没有手机的人了.响应设计给我们提供了一个巨大的机遇:站点的访问量的 ...
- 响应式网页设计代码_消除响应式网站建设设计中的缺陷
在过去的5年里,移动流量的份额增长了20%,现在响应式网站设计已经被认为是理所当然的了.到2020年,您可以通过手机几乎可以访问任何网站,它会很好用.大多数用户如果看到一个网站在他们的智能手机或平板电 ...
- UI设计中如何做响应式设计与自适应设计
UI设计中如何做响应式设计与自适应设计?由于科技在不断的发展,小伙伴们上网就不单单只依靠台式电脑了,还有平板电脑笔记本电脑都是可供大家选择的.面对不同的屏幕分辨率网站是如何进行适配的呢?今天AAA教育 ...
- HTML5期末大作业:旅游网站设计——简单大气的响应式旅游网页(5页) HTML+CSS+JavaScript...
HTML5期末大作业:旅游网站设计--简单大气的响应式旅游网页(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机毕设网页设计源码 常见网页 ...
- html网页设计期末大作业——响应式化妆品护肤品网页(11页) web课程设计 网页规划与设计
html网页设计期末大作业--响应式化妆品护肤品网页(11页) web课程设计 网页规划与设计 常见网页设计作业题材有 个人. 美食. 公司. 学校. 旅游. 电商. 宠物. 电器. 茶叶. 家居. ...
- HTML5期末大作业:电竞游戏网站设计——电竞游戏介绍响应式网页(7页) HTML+CSS+JavaScript
HTML5期末大作业:电竞游戏网站设计--电竞游戏介绍响应式网页(7页) HTML+CSS+JavaScript 常见网页设计作业题材有 个人. 美食. 公司. 学校. 旅游. 电商. 宠物. 电器. ...
- 意外的服务器响应_响应式系统reactive system初探
初识响应式系统 第一次听到reactive这个词还是在几年前,偶然了解到了Rxjava这个项目,仿佛为我打开了一扇新的大门,Rxjava是ReactiveX的java实现,ReactiveX家族除了R ...
最新文章
- SQL:安装多个实例,修改实例端口号,和IP加端口号连接实例
- httpsendrequest的head怎么用string写_商品广告语用怎么写?男人篇
- boost::range模块实现map_values相关的测试程序
- jvm虚拟机_一文入门jvm虚拟机
- lua transliterate实现(lua程序设计10.6练习10.3题)
- callback的实现
- 深入了解DSP和ARM的关系(相同与区别)
- 原生ajax请求超时处理,关于原生AJAX
- 抚琴成一快-电吉他内录(Zoom G3为例)
- Apollo canbus模块学习笔记
- CentOS 7.5 安装Nginx教程
- 005-浅谈SSDT
- 服务器电脑用哪个系统好,电脑系统哪个好用?电脑系统有几种版本
- 【赠书福利】人工智能发展的三驾马车
- 计算机初中毕业好学吗,初中毕业学计算机好学吗?
- java中批量下载图片(httpClient)
- QT Desinger设计程序主界面 PyUIC生成.py
- Windows10 64位 + caffe + Matlab -- cpu版本
- Linux Deepin 新增英国肯特大学镜像服务
- 数据挖掘(pandasxgboost)