当前位置: 首页 > news >正文

《Vue.js》阅读之响应式数据与副作用函数

Vue.js

《Vue.js设计与实现》(霍春阳)

  • 适合:从零手写Vue3响应式系统,大厂面试源码题直接覆盖。
  • 重点章节:第4章(响应式)、第5章(渲染器)、第8章(编译器)

因为我第一周的任务就是响应式原理(Proxy vs defineProperty),手写简易reactive,所以我是从第四章开始学习的。

4.1 响应式数据与副作用函数

1、副作用函数

函数的执行会直接或间接影响其他函数的执行

比如:

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

它的执行会使整个body的值都为hello vue3

2、响应式数据

01 const obj = { text: 'hello world' }
02 function effect() {
03   // effect 函数的执行会读取 obj.text
04   document.body.innerText = obj.text
05 }

如上面的这一段代码,我们希望当text发生变化的时候,effect会自动执行,这就是所谓的响应式数据

下面我们将会讲到这是怎么实现的:

● 当副作用函数 effect 执行时,会触发字段obj.text 的读取操作;
● 当修改 obj.text 的值时,会触发字段 obj.text的设置操作。
实现一个响应式的数据:拦截读和写两个步骤,
读的时候把effect函数放在一个容器里面
修改的时候就把这个effect函数释放出来
拦截一个对象的读取和设置操作:Proxy

  const data = { text: 'hello' }; // 定义数据对象const bucket = new Set(); // 设置一个容器用于存储副作用函数const obj = new Proxy(data, {// 读的时候拦截get(target, key) {bucket.add(effect); // 将当前活跃的副作用函数添加到容器return target[key]; // 返回属性值},// 拦截设置操作set(target, key, newVal) {target[key] = newVal;bucket.forEach(fn => fn()); // 执行所有存储的副作用函数return true;}});const  effect = () => {document.body.innerText = obj.text; // 副作用函数依赖于响应式数据};effect();setTimeout(() => {obj.text = "world"; // 修改数据,触发响应式更新}, 1000); // 延迟1秒修改数据

继续强化->我们现在硬编码了effect,但是如果副作用函数的名字不叫effect的话,这段代码就无法继续工作了

所以我们要提供一个用来注册副作用函数的机制

  // 用一个全局变量存储被注册的副作用函数let activeEffect;function effect (fn) {activeEffect = fn ; fn();}const bucket = new Set(); //设置一个容器const data = { text: 'world' }; // 初始化数据对象const obj = new Proxy(data , {//读的时候拦截放在容器里面get( tartget, key ){if(activeEffect){bucket.add(activeEffect);}return tartget[key];//返回属性值},set(target , key , newVal){// 拦截设置操作target[key] = newVal;bucket.forEach(fn => fn());return true;}})effect(() => {console.log('run');document.body.innerText = obj.text;})setTimeout(() => {obj.text = "hello"} , 1000)

当我们为obj添加新的属性的时候

  setTimeout(() => {obj.notExist = "hello vue3"} , 1000)

匿名副作用函数内没有读取这个新的属性的值,那么在1s之后不会起到写操作(即放出桶里面的所有函数),但是我尝试了一下,发现它执行了的。

我们为了解决这个问题,就要重新设计“桶”这个数据结构:让它无论读取的哪一个属性都会将副作用函数收到桶里面,设置属性的时候,无论设置的是哪一个属性,也都会将副作用函数取出并执行。

  let activeEffect;function effect (fn) {activeEffect = fn; fn();}const bucket = new WeakMap();const data = { text: 'world' }; // 确保所有属性都已定义const obj = new Proxy(data, {get(target, key){if(!activeEffect){return target[key];}// 根据tartget取来的depsMap,它是一个map类型let depsMap = bucket.get(target);// 如果不存在if(!depsMap){// 创建一个bucket.set(target, (depsMap = new Map()));}// 根据key取来的deps,它是一个set类型let deps = depsMap.get(key);// 如果不存在if(!deps){// 创建一个depsMap.set(key, (deps = new Set()));}deps.add(activeEffect); // 添加当前活跃的副作用函数return target[key];},set(target, key, newVal){target[key] = newVal;const depsMap = bucket.get(target);if(!depsMap){return;}const effects = depsMap.get(key);effects && effects.forEach(fn => fn()); // 只触发与键相关的副作用函数}});effect(() => {console.log('run');document.body.innerText = obj.text;});setTimeout(() => {obj.text = "hello vue3"; // 修改已定义的属性以触发依赖}, 1000);

大家可以发现,我们引用了weakMap(它与map最大的不同就是它对key是弱引用,不影响垃圾回收器的工作,通常存储只有当key所引用的对象存在时,才有价值的信息);

我们要解决的是属性不存在时候的问题,那么

  • 在读的时候判断是否有这个属性,没有就创建一个,有的话就把函数放在桶里面。
  • 在修改的时候也是要判断

最后,我们将一些函数进行封装:

<script setup>let activeEffect;function effect (fn) {activeEffect = fn; fn();}const bucket = new WeakMap();const data = { text: 'world' }; // 确保所有属性都已定义const obj = new Proxy(data, {get(target, key){track(target , key);return target[key];},set(target, key, newVal){target[key] = newVal;trigger(target , key , newVal);}});// 追踪变化function track(target , key){if(!activeEffect){return target[key];}// 根据tartget取来的depsMap,它是一个map类型let depsMap = bucket.get(target);// 如果不存在if(!depsMap){// 创建一个bucket.set(target, (depsMap = new Map()));}// 根据key取来的deps,它是一个set类型let deps = depsMap.get(key);// 如果不存在if(!deps){// 创建一个depsMap.set(key, (deps = new Set()));}deps.add(activeEffect); // 添加当前活跃的副作用函数}// 触发变化function trigger(target , key , newVal){const depsMap = bucket.get(target);if(!depsMap){return;}const effects = depsMap.get(key);effects && effects.forEach(fn => fn()); // 只触发与键相关的副作用函数}effect(() => {console.log('run');document.body.innerText = obj.text;});setTimeout(() => {obj.text = "hello vue3"; // 修改已定义的属性以触发依赖}, 1000);
</script>

相关文章:

  • Html5新特性_js 给元素自定义属性_json 详解_浅克隆与深克隆
  • 动态会话日志记录 ngx_stream_log_module
  • 介电测试的基本原理与方法及应用领域
  • 摆脱拖延症的详细计划示例
  • C——五子棋小游戏
  • 坐标系概述
  • 湖北理元理律师事务所:企业债务危机的“止血”与“造血”平衡术
  • spark的处理过程-转换算子和行动算子
  • 视频编码原理讲解一:VCL层和NAL层的讲解
  • 在文档里如何引用在线SVG甘特图
  • 17.three官方示例+编辑器+AI快速学习webgl_buffergeometry_lines
  • NVIDIA Quantum-2 QM9700系列利用400G infinniband扩展数据中心智能交换机 NVIDIA Quantum-2 InfiniBand 交换机系列
  • 服务器带宽基础知识
  • 量子加密通信:守护信息安全的未来之盾
  • 从逻辑学视角探索数学在数据科学中的系统应用:一个整合框架
  • 2024年北理工Python123第六章编程题整理
  • Linux `kill` 指令深度解析与高级应用指南
  • C38-全局变量、局部变量及外部变量
  • 吴恩达机器学习笔记:单变量线性回归
  • 机器学习驱动的智能化电池管理技术与应用
  • 生态环境保护督察工作条例对督察对象和内容作了哪些规定?有关负责人答问
  • 上海浦东机场1号、2号航站楼均推出国内出发安检24小时服务
  • 金科股份重整方案通过,正式进入重整计划执行环节
  • 成都锦江区一在建工地起火,致2人遇难1人受伤
  • 巴西总统卢拉昨晚抵达北京
  • 汉斯·季默:不会指挥的声音工程师终成音乐“大神”