Vue3响应式系统(二)

Vue3响应式系统(一)icon-default.png?t=N7T8https://blog.csdn.net/qq_55806761/article/details/135587077

六、嵌套的effect与effect栈。

        什么场景会用到effect嵌套呢?听我娓娓道来。

        就用Vue.js来说吧,Vue.js的渲染函数就是在effect中执行的:

/*Foo组件*/
const Foo = {
    render() {
        return /*.....*/
    }
}


// effect中执行Foo组件中的渲染函数
effect(() => {
    Foo.render()
})

        当组件发生嵌套的时候,渲染的时候effect函数中就会发生effect嵌套。

//Bar组件
const Bar = {
  render() {/* ... */}
}
const Foo = {
  render() {
    return <Bar />
  }
}

//effect执行的时候就会发生嵌套
effect(() => {
  Foo.render()
  effect(() => {
    Bar.render()
  })
})

        所以,目前并不能支持effect嵌套,我们用之前的代码来测试一下:

//原始数据
const data = {
  ok: true,
  text: 'hello world'
}
// WeakMap桶
const bucketMap = new WeakMap()
//用全局变量存储被注册的副作用函数
let activeEffect

function effect(fn) {
  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    fn()
  }
  // deps用来存储所有与这副作用函数相关联的依赖集合
  effectFn.deps = []
  effectFn()
}
const obj = new Proxy(data, {
  get(target, key) {
    // 注册副作用函数
    track(target, key)
    return target[key]
  },
  set(target, key, newVal) {
    target[key] = newVal
    // 触发副作用函数
    trigger(target, key)
    return true
  }
})
function cleanup(effectFn) {
  //遍历effectFn的deps数组
  for(let i = 0; i < effectFn.deps.length; i++) {
    let deps = effectFn.deps[i]
    deps.delete(effectFn)
  }
  // 最后需要重置effectFn.deps数组
  effectFn.deps.length = 0
}
function trigger(target, key) {
  // 取target
  const depsMap = bucketMap.get(target)
  if (!depsMap) return
  // 根据key取副作用函数
  const effects = depsMap.get(key)
  // 执行副作用函数
  const effectToRun = new Set(effects)
  effectToRun && effectToRun.forEach(fn => fn())
}
function track(target, key) {
  // 没有activeEffect直接返回
  if (!activeEffect) return target[key]
  // 取出WeakMap桶里的值 target ===> key
  let depsMap = bucketMap.get(target)
  // 如果不存在depsMap,那就新建Map与target建立联系
  if (!depsMap) {
    bucketMap.set(target, (depsMap = new Map()))
  }
  // key ===> effectFn
  let deps = depsMap.get(key)
  if (!deps) {
    depsMap.set(key, deps = new Set())
  }
  // 注册副作用函数
  deps.add(activeEffect)
  // ======= 主要就是增加关联数组中 ===========
  activeEffect.deps.push(deps)
}

let temp1, temp2
effect(function effectFn1() {
  console.log('effectFn1执行')
  effect(function effectFn2() {
    console.log('effectFn2执行');
    temp2 = obj.ok
  })
  temp1 = obj.text
})
obj.text = 'Vue3'

 

        出现问题了,当我们修改text的值的时候,我们希望的是触发effectFn1,而现在是触发effectFn2,并没有执行effectFn1。

       问题在哪里呢?

        我们用全局变量activeEffect来存储通过effect函数注册的副作用函数,意味着同一时刻activeEffect所存储的副作用函数只能有一个。当副作用函数发生嵌套时,内层副作用函数会覆盖activeEffect,并且永远不会恢复,即使响应式数据是在外层副作用函数中读取的,他们收集到的副作用函数也都会是内层副作用函数,这个就是问题所在。

        为了解决这个问题,我们需要一个副作用函数栈effectStack,在副作用函数执行的时候,将当前副作用函数压入栈中,执行完毕后将副作用函数弹出,activeEffect始终指向栈顶的副作用函数。这样便不会出现相互影响的情况了。

       增加副作用函数栈

//用全局变量存储被注册的副作用函数
let activeEffect
// effectStack栈
let effectStack = []
function effect(fn) {
  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    fn()
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
  }
  // deps用来存储所有与这副作用函数相关联的依赖集合
  effectFn.deps = []
  effectFn()
}

        完整代码:

//原始数据
const data = {
  ok: true,
  text: 'hello world'
}
// WeakMap桶
const bucketMap = new WeakMap()
//用全局变量存储被注册的副作用函数
let activeEffect
// effectStack栈
let effectStack = []
function effect(fn) {
  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    fn()
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
  }
  // deps用来存储所有与这副作用函数相关联的依赖集合
  effectFn.deps = []
  effectFn()
}
const obj = new Proxy(data, {
  get(target, key) {
    // 注册副作用函数
    track(target, key)
    return target[key]
  },
  set(target, key, newVal) {
    target[key] = newVal
    // 触发副作用函数
    trigger(target, key)
    return true
  }
})
function cleanup(effectFn) {
  //遍历effectFn的deps数组
  for(let i = 0; i < effectFn.deps.length; i++) {
    let deps = effectFn.deps[i]
    deps.delete(effectFn)
  }
  // 最后需要重置effectFn.deps数组
  effectFn.deps.length = 0
}
function trigger(target, key) {
  // 取target
  const depsMap = bucketMap.get(target)
  if (!depsMap) return
  // 根据key取副作用函数
  const effects = depsMap.get(key)
  // 执行副作用函数
  const effectToRun = new Set(effects)
  effectToRun && effectToRun.forEach(effectFn => effectFn())
}
function track(target, key) {
  // 没有activeEffect直接返回
  if (!activeEffect) return target[key]
  // 取出WeakMap桶里的值 target ===> key
  let depsMap = bucketMap.get(target)
  // 如果不存在depsMap,那就新建Map与target建立联系
  if (!depsMap) {
    bucketMap.set(target, (depsMap = new Map()))
  }
  // key ===> effectFn
  let deps = depsMap.get(key)
  if (!deps) {
    depsMap.set(key, deps = new Set())
  }
  // 注册副作用函数
  deps.add(activeEffect)
  // ======= 主要就是增加关联数组中 ===========
  activeEffect.deps.push(deps)
}

let temp1, temp2
effect(function effectFn1() {
  debugger
  console.log('effectFn1执行')
  effect(function effectFn2() {
    console.log('effectFn2执行');
    temp2 = obj.ok
  })
  temp1 = obj.text
})
obj.text = 'Vue3'

        

        这样便达到预期!! 

Vue3响应式系统(三)icon-default.png?t=N7T8https://blog.csdn.net/qq_55806761/article/details/135635322

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/329422.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

香港服务器托管:你对服务器托管了解多少?

在当今数字化的时代&#xff0c;服务器托管已成为企业和网站运营的关键一环。对于许多企业来说&#xff0c;如何选择一个安全、稳定、高效的服务器托管方案&#xff0c;成为了确保业务连续性和数据安全的重要课题。那么&#xff0c;究竟什么是服务器托管&#xff0c;它又有哪些…

SpringBoot多环境配置与添加logback日志

1、多环境配置 一个项目会有多个运行环境 所以SpringBoot提供了可以适应多个环境的配置文件 每个文件对应一个端口号 application-dev.yml 开发环境 端口8090 application-test.yml 测试环境 端口8091 application-prod.yml 生产环境 端口8092 在application中选择使用哪个…

Qt6入门教程 8:信号和槽机制(连接方式)

目录 一.一个信号与槽连接的例子 二.第五个参数 1.Qt::AutoConnection 2.Qt::DirectConnection 3.Qt::QueuedConnection 4.Qt::BlockingQueuedConnection 5.Qt::UniqueConnection 三.信号 四.connect函数原型 五.信号与槽的多种用法 六.槽的属性 一.一个信号与槽连接…

MetaGPT入门(一)

本文在Win11操作系统下进行&#xff0c;工具pycharm 一、环境准备 1.建议使用conda虚拟环境 安装anaconda参考&#xff1a;Windows10下Anaconda的安装_windows anaconda 路径-CSDN博客 打开Anaconda Powershell Prompt命令窗口&#xff0c;输入下面命令&#xff0c;创建3.1…

MyBatisPlus学习笔记三-核心功能

接上篇&#xff1a; MyBatisPlus学习笔记二-CSDN博客 1、核心功能-IService开发基础业务接口 1.1、介绍 1.2、引用依赖 1.3、配置文件 1.4、用例-新增 1.5、用例-删除 1.6、用例-根据id查询 1.7、用例-根据ids查询 2、核心功能-IService开发复杂业务接口 2.1、实例-更新 3、…

Spring高手之路-Spring在业务中常见的使用方式

目录 通过IOC实现策略模式 通过AOP实现拦截增强 1.参数检验 2.缓存逻辑 3.日志记录 通过Event异步解耦 通过Spring管理事务 1.声明式事务 2.编程式事务 3.需要注意的问题 不能在事务中处理分布式缓存 不能在事务中执行 RPC 操作 不过度使用声明式事务 通过IOC实现…

软件工程应用题汇总

绘制数据流图(L0/L1/L2) DFD/L0&#xff08;基本系统模型&#xff09; 只包含源点终点和一个处理(XXX系统) DFD/L1&#xff08;功能级数据流图&#xff09;在L0基础上进一步划分处理(XXX系统) 个人理解 DFD/L2&#xff08;在L1基础上进一步分解后的数据流图&#xff09; 数据…

蓝桥杯备赛 day 3 —— 高精度(C/C++,零基础,配图)

目录 &#x1f308;前言&#xff1a; &#x1f4c1; 高精度的概念 &#x1f4c1; 高精度加法和其模板 &#x1f4c1; 高精度减法和其模板 &#x1f4c1; 高精度乘法和其模板 &#x1f4c1; 高精度除法和其模板 &#x1f4c1; 总结 &#x1f308;前言&#xff1a; 这篇文…

C#中对浮点数NaN,PositiveInfinity,NegativeInfinity的特殊处理

NAN NAN 整体意思为Not a Number 不是一个数&#xff0c; NaN&#xff08;Not a Number&#xff0c;非数&#xff09;是计算机科学中数值数据类型的一类值&#xff0c;表示未定义或不可表示的值。常在浮点数运算中使用。首次引入NaN的是1985年的IEEE 754浮点数标准。 EEE 75…

Linux Mii management/mdio子系统分析之六 fixed-mii_bus分析(mac2mac分析)

&#xff08;转载&#xff09;原文链接&#xff1a;[https://blog.csdn.net/u014044624/article/details/130674908] (https://blog.csdn.net/u014044624/article/details/130674908) 前面几章我们介绍了MDIO模块的大部分内容&#xff0c;针对mii_bus、mdio_bus、phy_device、p…

学习鸿蒙先解决这几个是关键问题~

HarmonyOS 是最近最火的操作系统&#xff0c;HarmonyOS 宣布删除 Android 代码之后&#xff0c;正式向世界上第三大操作系统有迈进了一步&#xff0c;HarmonyOS 前期为了完成从 Android 到 HarmonyOS 的过渡&#xff0c;在设计之初 HarmonyOS 采用了双框架架构设计。 从图中可以…

【栈】【字符串和int类型转化】Leetcode 150 逆波兰表达式求值

【栈】【字符串和int类型转化】Leetcode 150 逆波兰表达式求值 解法1 栈 ---------------&#x1f388;&#x1f388;题目链接 Leetcode 150 逆波兰表达式求值 &#x1f388;&#x1f388;------------------- 解法1 栈 字符串转化为int类型数据: Integer.parseInt(s) Long.p…

SpringBoot教程(十五) | SpringBoot集成RabbitMq

SpringBoot教程(十五) | SpringBoot集成RabbitMq RabbitMq是我们在开发过程中经常会使用的一种消息队列。今天我们来研究研究rabbitMq的使用。 rabbitMq的官网&#xff1a; rabbitmq.com/ rabbitMq的安装这里先略过&#xff0c;因为我尝试了几次都失败了&#xff0c;后面等我…

FPGA时序分析与时序约束(四)——时序例外约束

目录 一、时序例外约束 1.1 为什么需要时序例外约束 1.2 时序例外约束分类 二、多周期约束 2.1 多周期约束语法 2.2 同频同相时钟的多周期约束 2.3 同频异相时钟的多周期约束 2.4 慢时钟域到快时钟域的多周期约束 2.5 快时钟域到慢时钟域的多周期约束 三、虚假路径约…

网站SEO优化方案

1&#xff0c;去各类搜索引擎里面&#xff0c;注册你的站点 解决方案&#xff1a;注册地址&#xff1a;https://seo.chinaz.com/chinaz.com 2&#xff0c;网站地址使用 https 会增加搜索排名 解决方案&#xff1a;https:www.xxx.com 3&#xff0c;官网每个页面的 meta 里面&a…

牛客周赛 Round 10 解题报告 | 珂学家 | 三分模板 + 计数DFS + 回文中心扩展

前言 整体评价 T2真是一个折磨人的小妖精&#xff0c;写了两版DFS&#xff0c;第二版计数DFS才过。T3是三分模板&#xff0c;感觉也可以求导数。T4的数据规模才n1000&#xff0c;因此中心扩展的 O ( n 2 ) O(n^2) O(n2)当仁不让。 A. 游游的最长稳定子数组 滑窗经典题 从某个…

78、avx2 数据 load/store 向量化操作介绍

向量寄存器和一个最简单的寄存器-内存的存储器模型,查看上一节。 本节基于整个内存模型,介绍一下如何使用 avx2 向量指令集,来完成数据从内存到寄存器中的交互的。 load 操作 在改内存模型下,load 操作指将数据从内存中加载到寄存器中。 使用 C++ 代码实现如下: float…

REVIT二次开发修改轴网

REVIT二次开发修改轴网 步骤1 步骤2 步骤3 功能实现在这 using System; using System.Collections.Generic; using System.Linq; using

【实操】基于 GitHub Pages + Hexo 搭建个人博客

《开发工具系列》 【实操】基于 GitHub Pages Hexo 搭建个人博客 一、引言二、接入 Node.js2.1 下载并安装 Node.js2.2 环境变量配置 三、接入 Git3.1 下载并安装 Git3.2 环境变量配置 四、接入 Hexo4.1 安装 Hexo4.2 建站4.3 本地启动服务器 五、接入 GitHub Pages5.1 初识 G…

大模型学习与实践笔记(七)

一、环境配置 1.平台&#xff1a; Ubuntu Anaconda CUDA/CUDNN 8GB nvidia显卡 2.安装 # 构建虚拟环境 conda create --name xtuner0.1.9 python3.10 -y # 拉取 0.1.9 的版本源码 git clone -b v0.1.9 https://github.com/InternLM/xtuner# 从源码安装 XTuner pip insta…