解读vue3源码-响应式篇3 effect副作用函数

提示:看到我 请让我滚去学习

文章目录

  • 前言
  • effect问题拓展
    • 分支切换与 cleanup
    • 嵌套的 effect 与 effect 栈
    • 解决在副作用函数中同时读取和操作同一属性时无限循环
  • effect函数实现
    • computed-api 实现图解
    • 在这里插入图片描述
  • 总结


前言

什么是副作用函数?

在 Vue 3 中,副作用函数(Effect Function)通常指的是那些依赖于响应式数据并在数据变化时重新执行的函数。Vue 3 引入了 ref 和 reactive API 来创建响应式数据,以及 effect 函数来观察和处理这些数据的变化。

当一个函数被标记为副作用函数时,它会被 Vue 的响应式系统跟踪。这意味着每当这个函数依赖的响应式数据发生变化时,Vue 会自动重新执行这个函数,从而触发 UI 的更新或其他相关操作。

例如:
import { ref, effect } from 'vue';
const count = ref(0);
// 定义一个副作用函数
effect(() => {console.log('Count changed:', count.value);
});
// 更改count的值,副作用函数将被重新执行
count.value++;

effect问题拓展

以我们的精简版本的watchEffect 函数做模本例子,我们将对其进行拓展

  function watchEffect (effect) {activeEffect = effecteffect()}

全局targetMap存储结构:
在这里插入图片描述

分支切换与 cleanup

我们就简单的watchEffect ,会有很多问题,比如下面的示例:

  const data = { ok: true, a: 111, b: 222 }const obj = reactive(data)watchEffect(function effectFn () {console.log(obj.ok ? obj.a : obj.b)})//111obj.ok = false  //222obj.a = 3 //222

我们使用一个三元表达式,第一次初始化watchEffect函数,因为obj.ok 为true,所以访问的是obj.ok和obj.a,那么全局targetMap如下:
在这里插入图片描述
当我们修改obj.ok的值后,重新触发watchEffect函数,再次访问内部依赖,因为obj.ok值改变,所以访问的属性变成了obj.ok和obj.b,这样obj.b就会收集到targetMap中,全局targetMap如下:
在这里插入图片描述
这样就有了问题,我们最新的effect其实是ok和b的依赖,但是依赖表中存储了ok、a、b三个属性,所以当我们访问a时,还是会触发watchEffect重新执行。
解决这个问题的思路很简单,每次副作用函数执行时,我们可以先把它从所有与之关联的依赖集合中删除,要将一个副作用函数从所有与之关联的依赖集合中移除,就需要明确知道哪些依赖集合中包含它。所以我们在响应式数据访问收集副作用函数时,需要给副作用函数添加一个dep[]属性,然后将当前触发依赖收集的属性push到此dep中。
在这里插入图片描述
那我们来看vue3源码实现:
其实过程就是:实现双向记忆(运行effect时,找到所有绑定这个effect的属性,删除所有属性上的此effect)
1.首先ReactiveEffect类中定义了属性_depsLength,这个属性是为了记录当前收集的dep是同一个effect中的第几个响应式属性
在这里插入图片描述
在run方法中每次执行副作用函数前都会调用preCleanupEffect方法,这个方法就是重置当前effect内部的指针,_trackId用于记录当前effect实例是第几次运行,_depsLength表示当前是第几个dep收集,每次重走effect方法,都会重置_depsLength为0,表示从第1个开始收集
在这里插入图片描述
在这里插入图片描述
而在track函数执行收集依赖时,我们看下effect是怎么记录dep的
在这里插入图片描述
这里有一个简单的diff算法,我们知道_depsLength用来记录当前dep是effect方法中第几个记录的,比如我在上述示例:

 watchEffect(function effectFn () {console.log(obj.ok ? obj.a : obj.b)})

当obj.ok为true时,第一次进入watchEffect,effectFn副作用函数中监听的值为ok和a,那么effectFn的deps[]中就是[ok,a],当我们修改ok的值,就会触发effectFn执行,effectFn中再次访问值为ok和b,这时候会按照顺序将新值和旧值按顺序对比,第一个dep不变是ok,然后用b替换a。

  const stop=effect(function effectFn111 () {console.log('11111', obj.a, obj.a)})

此处_trackId用于记录当前effect副作用函数运行次数,if (dep.get(effect2) !== effect2._trackId) {}。可以过滤到上面这种一个函数中多次使用一个变量的情况

此时还会有一个新问题,那么如果第一次副作用函数绑定了2个属性上,但是第二次运行时只需要绑定1个了,例如

 watchEffect(function effectFn () {console.log(obj.ok ? obj.a : null)})

那么在执行完替代后需要根据新列表数量,删除多余列表长度,实现代码如下:
在这里插入图片描述
vue3.0是使用set存储effect依赖,但是set是无序的,而这也是为什么改成有序的map存储effects的原因之一。

嵌套的 effect 与 effect 栈

我们vue3是可以支持组件嵌套的,所以本质effect也是应该要支持嵌套,例如:

effect(function effectFn1() {effect(function effectFn2() { /* ... */ })console.log(pro.a)})

但是这样就会有问题,像上面这样写,pro.a绑定的副作用函数是effectFn2并非effectFn1,这是因为我们用全局变量 activeEffect 来存储通过 effect 函数注册的副作用函数,这意味着同一时刻 activeEffect 所存储的副作用函数只能有一个。当副作用函数发生嵌套时,内层副作用函数的执行会覆盖 activeEffect 的值,并且永远不会恢复到原来的值。这时如果再有响应式数据进行依赖收集,即使这个响应式数据是在外层副作用函数中读取的,它们收集到的副作用函数也都会是内层副作用函数,这就是问题所在。
而我们的代码中只需要在effect运行之初记录上次的effect,在副作用函数执行完毕时,恢复上次的effect就行。
在这里插入图片描述

解决在副作用函数中同时读取和操作同一属性时无限循环

  const data = { ok: true, a: 111, b: 222 }const obj = reactive(data)watchEffect(function effectFn111 () {console.log('11111', obj.a++)})obj.a = 666

这种在副作用函数中既读取又操作一个属性会爆栈,这是因为首先读取 obj.a的值,这会触发 track 操作,将当前副作用函数收集到“桶”中,接着将其加 1 后再赋值给 obj.a,此时会触发 trigger 操作,即把“桶”中的副作用函数取出并执行。但问题是该副作用函数正在执行中,还没有执行完毕,就要开始下一次的执行。这样会导致无限递归地调用自己,于是就产生了栈溢出。
解决方案,在trigger触发时判断要执行的副作用函数是否正在执行,正在执行就跳过。
vue3代码通过_running变量控制在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

effect函数实现

effect函数原本在vue3.0中是没有暴露的,vue官方文档并没有相关说明,那么我们看看它是怎么实现的。
在这里插入图片描述

以上就是effect函数的实现,接收两个参数,一个是运行时的副作用函数,另一个options,主要作用如下:
在这里插入图片描述
这个函数就是实例化并抛出了ReactiveEffect类,并且根据参数运行一次对象的run方法,那我们进入ReactiveEffect中看看ReactiveEffect是怎么实现的
在这里插入图片描述
ReactiveEffect主要方法是包含
初始化方法,
get diety()和set dirty()属性访问器(主要用于记录computed-api是否需要重新计算),
run方法,
stop方法:停止effect,watch函数返回值即stop函数,执行停止监听。

我们先看看构造方法中做了什么
在这里插入图片描述
fn:即我们传入的副作用函数,我们将其赋值给class内部属性,可以预见其在我们的run函数触发的时候执行,
shouldSchedule:为调度器,默认为false,如果外部传入,我们用shouldSchedule来执行副作用函数
active:标识创建的effect是响应式的,若为false就不会赋值activeEffect关联属性
dirtyLevel:计算属性使用,记录是否是脏数据
trackId:用于记录当前effect执行了几次
runnings:是否正在执行
deps:当前副作用函数绑定的dep,属性和effect做双向记忆
deplength:用来标识当前属性是同一个effect中收集第几个依赖

我们看run方法,也就是执行方法。这个方法其实主要就是做了2件事:
1.将这个effect副作用函数赋值给全局的activeEffect,在响应式数据触发读取的时候,和数据访问属性进行绑定
2.执行一次副作用函数
在这里插入图片描述

至此总体过一遍响应式流程:
例子:effect(function fn(){ proxya.name })
1.effect函数运行创建ReactiveEffect对象,运行其run方法。
2.run方法将ReactiveEffect赋值给全局的activeEffect对对象,并且运行执行方法fn,
3.fn方法中访问proxya.name,触发name属性拦截,触发依赖收集将activeEffect(即fn)和proxya.name绑定。
4.effect运行完毕全局activeEffect赋值为上个堆栈的activeEffect对象(此处为undefined)
5.修改proxya.name值,触发set拦截,触发依赖执行proxya.name绑定的effect(即3中绑定的ReactiveEffect—fn方法)

computed-api 实现图解

在这里插入图片描述

总结

至此我们就能大体了解响应式数据的实现,而对于一些衍生的api,如watch、watchEffecte、computed等我们都可自行阅读源码,其本质都是使用RactiveEffect类实现。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SCYC 56901传感器SCYC 56901模块面价

SCYC 56901传感器SCYC 56901模块面价 SCYC 56901传感器SCYC 56901模块面价 SCYC 56901传感器SCYC 56901模块面价 SCYC 56901传感器SCYC 56901模块引脚线 SCYC 56901传感器SCYC 56901模块说明书 SCYC 56901传感器SCYC 56901模块电路图 SCYC 56901温度传感器是早开发&#…

iPhone 手机使用技巧:iPhone 数据恢复软件

无论是由于意外删除、系统崩溃还是软件更新,丢失 iPhone 上的数据都是一场噩梦。从珍贵的照片到重要的工作文件,这种损失可能会让人感到毁灭性。值得庆幸的是,几个 iPhone 数据恢复软件选项可以帮助您找回丢失的文件。这些工具提供不同的功能…

神经网络——非线性激活

1 非线性激活 1.1 几种常见的非线性激活: ReLU (Rectified Linear Unit)线性整流函数 Sigmoid 1.2代码实战: 1.2.1 ReLU import torch from torch import nn from torch.nn import ReLUinputtorch.tensor([[1,-0.5],[-1,3]])inputtorch.reshape(…

【计算机网络】名词解释--网络专有名词详解

在网络通信中,有许多专业术语和概念,它们共同构成了网络通信的基础。以下是一些常见的网络术语及其定义和相互之间的关系: 一、网络基础 1.1 电路交换:电路交换是一种在数据传输前建立专用通信路径的通信方式。在通信开始前&…

HTML+CSS+JavaScript制作动态七夕表白网页(含音乐+自定义文字)

源码介绍 这篇博客就享下前端代码如何实现HTMLCSSJavaScript制作七夕表白网页(含音乐自定义文字)。记事本打开源码文件可以进行内容文字之类的修改,双击html文件可以本地运行效果,也可以上传到服务器里面,重定向这个界面 源码效果 源码下载…

从行为面试问题(behavioral questions)看中美程序员差异。

中美程序员在职场中的工作状态和职能、福利等有很大区别,从面试中的BQ轮就可见一斑。 中美程序员的面试轮差异? 国内的面试轮在不同公司间差异很大,但总体的问题类型包含笔试面试(算法题、概念题、项目深挖、职业目标、职场文化…

混合现实UI优化:利用物理环境的直接交互

随着虚拟现实(VR)和混合现实(MR)技术的发展,用户界面(UI)的设计变得越来越重要,尤其是在需要适应多种物理环境的情况下。本文将介绍一种名为 InteractionAdapt 的用户界面优化方法,它专为VR环境中的工作空间适配而设计,能够有效利用物理环境,为用户提供更加灵活和个…

layui table表单 checkbox选中一个其它也要选中

当我们选中其中一个商品的时候同类型的商品状态也要跟着改变 所以要在表单加载完成后去监听checkbox ,done:function (res) {console.log(详情表格数据,res)tableDetailList res.data;// 监听表格复选框选择table.on(checkbox( INST_SELECTORS.instLayFilters.unpaidTableDe…

Canvas 动画: atan2 三角函数与鼠标跟随效果

这个案例展示了如何使用HTML5的Canvas和JavaScript实现一个动态效果:在画布上绘制一个箭头,并让它实时跟随鼠标移动。这个小项目不仅有趣,还能帮助你理解编程和基本数学概念的实际应用。 项目需求 我们的目标是在一个画布上绘制一个箭头&…

HTML+CSS浮动和清除浮动的效果及其应用场景举例

一、清除浮动的效果 解释 .container:用于展示浮动和清除浮动效果的容器,具有边框和背景色以便于区分。 .float-box:浮动元素,用不同的背景色标识。 .clearfix:使用伪元素清除浮动的类,应用于第二个容器。 …

电脑浏览器打不开部分网页

电脑浏览器打不开部分网页 时间: 2024-08-25 问题描述: 电脑突然打不开部分网页 例如腾讯文档 夸克网盘 但其他网页能够正常打开 原因 可能为域名解析问题 更改DNS即可解决 解决办法 控制面板–> 网络和Internet—>网络连接—> WLAN----> 属性 —> Interne…

一元四次方程求解-【附MATLAB代码】

目录 前言 求解方法 MATLAB验证 附:一元四次方程的故事 前言 最近在研究机器人的干涉(碰撞)检测,遇到了一个问题,就是在求椭圆到原点的最短距离时,构建的方程是一个一元四次方程。无论是高中的初等数学…

文件包含漏洞案例

一、PHP://INPUT Example 1&#xff1a;造成任意代码执行 源代码&#xff1a; <meta charset"utf8"> <?php error_reporting(0); $file $_GET["file"]; if(stristr($file,"php://filter") || stristr($file,"zip://") |…

批量整理文件夹的得力助手:让杂乱无章变得井井有条

在日常工作和生活中&#xff0c;我们经常会遇到这样一个问题&#xff1a;电脑或移动硬盘里的文件夹越积越多&#xff0c;文件散落各处&#xff0c;找起来既费时又费力。 有时候&#xff0c;为了找一个文件&#xff0c;你可能得在无数个文件夹中来回穿梭&#xff0c;仿佛进行了…

C++ 设计模式——命令模式

C 设计模式——命令模式 C 设计模式——命令模式主要组成部分构建过程命令模式 UML 图UML 图解析 命令模式的优点命令模式的缺点命令模式适用场景总结完整代码 C 设计模式——命令模式 命令(Command)模式是一种行为型模式&#xff0c;它将请求封装为对象&#xff0c;从而使您能…

数据中台架构设计

由于当前项目需要对接多个不同的数据源&#xff0c;同时涉及到多端处理&#xff0c;而且需要考虑海量数据处理&#xff0c;还有总部与分部架构部署问题&#xff0c;因而整体技术栈倾向于大数据和分表分库式处理数据层接入问题。 简单讲&#xff0c;项目分为数据中台和业务中台…

[新手入门]1台电脑+1个电视+2个软件(sunshine+moonlight) 解决黑神话悟空没有hdmi线的痛...

sunshinemoonlight 解决黑神话悟空 本地串流投屏 背景:偶然间在B站发现了sunshinemoonlight方案,替代hdmi线,做本地串流...于是心灵手巧的我开始尝试踩坑之路:1.准备安装包2.开始安装2.1 笔记本windows安装sunshine2.2 遇到了第一个坑.Fatal: ViGEmBus is not installed or run…

docke进阶---镜像迁移、容器的ip地址、端口映射和持久化

1.镜像的迁移 1.镜像打包 #查看镜像有一个centos的镜像 [rootdocker0 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE centos latest 5d0da3dc9764 2 years ago 231MB 3查看帮助文件 docker --help save Save one or more…

【Unity】移动端草海解决方案

草海是开放大世界渲染的必不可少的因素&#xff0c;Unity 原生的 Terrain 草海效率较低&#xff0c;而且无法与 RVT 结合起来&#xff0c;无法在移动端上实现。因此我们自己搓出来一套草海系统&#xff0c;使用 C# 多线程辅助运算&#xff0c;并能支持割草、烧草等进阶玩法。草…

数据库内容保密检查系统:及时发现“潜在”安全威胁

日前&#xff0c;国内专注于保密与非密领域的分级保护、等级保护、业务连续性安全和大数据安全产品解决方案与相关技术研究开发的领军企业——国联易安自主研发的国联数据库内容保密检查系统V1.0通过国保局涉密检测&#xff0c;获得涉密信息系统产品检测证书。其主要具备以下主…