customRef 与 ref

ref() 我们已经很熟悉了,就是用来定义响应式数据的,其底层原理还是通过 Object.defineprotpty 中的 get 实现收集依赖( trackRefValue 函数收集),通过 set 实现分发依赖通知更新( triggerRefValue 函数分发 )。我们看看 ref 的源码就知道了

class RefImpl {private _value: any;    // 用来存储响应值private _rawValue: any;    // 用来存储原始值public dep?: Dep = undefined;    // 用来收集分发依赖public readonly __v_isRef = true;    //是否只读,暂不考虑// 接收 new RefImpl() 传递过来的 rawValue 和 shallow  constructor(value, public readonly __v_isShallow: boolean) {// 判断是否需要深层响应,如果不用,直接返回 Value 值,如果需要深层响应,则调用 toRaw 函数解除 value 的响应式,将其转化为原始值,以保证后续的深层响应this._rawValue = __v_isShallow ? value : toRaw(value);// 判断是否需要深层响应,如果不用,则直接返回Value,不做响应式处理。如果需要深层响应,则调用 reactive 函数进行深层响应this._value = __v_isShallow ? value : reactive(value);}get value() {// 收集依赖trackRefValue(this);// 返回响应式数据return this._value;}set value(newVal) {// 将 newVal 转化为原始值,并于初始原始值比较,若不同,则准备更新数据,渲染页面,分发依赖if (hasChanged(toRaw(newVal), this._rawValue)) {//判断是否需要深层响应,如果不用,直接返回 newVal 值,如果需要深层响应,则调用 toRaw 函数解除 newVal 的响应式,将其转化为原始值,以保证后续的深层响应this._rawValue = this.__v_isShallow ? newVal : toRaw(newVal);// 判断是否需要深层响应,如果不用,则直接返回Value,不做响应式处理。如果需要深层响应,则调用 reactive 函数进行深层响应this._value = this.__v_isShallow ? newVal : reactive(newVal);// 分发依赖,通知更新triggerRefValue(this);}}
}

具体的关于 ref 的使用以及更深层的理解请参考之前的文章 -- ref 函数

那么这个 customRef 函数是用来干啥的呢?

customRef

概念:创建一个自定义的 ref 函数,在其内部显式声明对其依赖追踪和更新触发的控制方式。

前面一句好理解,创建一个自定义的 ref ,其类型是一个函数,函数体内部的逻辑内容自定义。

后面一句就有点绕了,显式声明对其依赖追踪和更新触发的控制方式该怎么理解呢?

我们看看 ref 就知道了,当我们调用 ref 之后,读取数据时,Vue 底层就会自动去在 get 中收集依赖。修改数据时,会自动在 set 中分发依赖。这是不需要我们关心的,我们只需要调用 ref 函数就可以实现了。

但是 customRef 并没有按照 ref 的逻辑去实现,customRef 的处理是:既然你都自定义了,那你就自定义完整一点,依赖收集和分发工作你也自己做了,别去麻烦 Vue 底层在给你适配转化一次。

用法:customRef() 预期接收一个工厂函数作为参数,这个工厂函数接受 track 和 trigger 两个函数作为参数,并返回一个带有 get 和 set 方法的对象。

按照官网的例子我们一点点实现优化:创建一个自定义 ref ,实现防抖。具体效果就是我在 input 框中输入值,延时展示值。

第一步:不延迟,直接同步展示,v-model 双向绑定数据,插值语法展示数据,setup 定义数据

<template><input type="text" v-model="keyword"><h3>{{keyword}}</h3>
</template><script>import {ref} from 'vue'export default {name:'Demo',setup(){let keyword = ref('hello') //使用Vue准备好的内置refreturn {keyword}}}
</script>

展示效果:

第二步:定义自己的 ref 函数,并且使用它

setup(){// let keyword = ref('hello') //使用Vue准备好的内置ref// 定义自己的 ref 函数,接收值,并 return 出具体的值,否则返回undefinedfunction myRef(value) {console.log(value);return value}let keyword = myRef('hello')  // 使用自定义的 ref 函数return {keyword}
}

此时我们发现,当我们使用自定义 ref 函数 时,因为我们并没有对这个数据进行响应式处理,所以页面数据并没有同步更新,这个时候我们就需要用到  customRef 来实现内部的逻辑。

第三步:调用 customRef 实现内部逻辑,按照 customRef() 的使用方法,完善 myRef()

这是 vscode 插件的提示语法,可以看到 customRef() 的完整用法。所以,我们完善一下 myRef()

function myRef(value) {return customRef((track,trigger) => {return {get() {// ...},set() {// ...}}})
}

到了这一步是不是就很眼熟了,这不就是 ref() 函数里面的响应式么,取值调 get,修改调 set。按照想法实现一下

function myRef(value) {return customRef((track,trigger) => {return {get() {return value},set(newValue) {value = newValue}}})
}

虽然数据发生了变更,但是页面并没有同步更新

这是因为数据只是发生了变更,但是并没有实现依赖追踪和触发更新,这个时候,我们在看看 ref() 的源码。

get value() {// 收集依赖trackRefValue(this);// 返回响应式数据return this._value;}set value(newVal) {// 判断逻辑 ......// 更新数据this._value = this.__v_isShallow ? newVal : reactive(newVal);// 分发依赖,通知更新triggerRefValue(this);}

在 ref() 中,在get 中收集依赖,在 set 中分发依赖,按这个模式,我们在 customRef() 中的 get 和 set 中也应该收集或分发。而 customRef 接收的工厂函数接收  track 和 trigger 两个函数作为参数,这两个函数其实就是对应的 ref() 中的 trackRefValue() 和 triggerRefValue() ,于是完善后的代码就成了这样。

function myRef(value) {return customRef((track,trigger) => {return {get() {track()    // 先收集依赖,告诉Vue 这个 value 值是需要被追踪的return value    // 然后返回被追踪的值,此时Vue底层已经对 value 实现了追踪},set(newValue) {value = newValue    // 先设置值,因为 value 被追踪,所以数据改变时,Vue底层是能监听到trigger()    // 然后分发依赖,告诉 Vue 需要更新界面}}})
}

实现的效果

到了这里,其实我们就完成了与第一步同样的效果:不延迟,直接同步展示。

剩下的就是实现防抖了。当数据改变时,我们通过 setTimeout 我们可以实现延迟 500ms 展示值。

set(newValue) {setTimeout(() => {value = newValue;trigger();}, 500);
},

 

但是我们发现,当过快输入时,值出现了诡异的变动,会突然卡一下,这是因为,每次改变数据时,都会开启一个定时器,但是定时器却并没有清除,这就导致累计了多个定时器才会出现这种情况。 

按照标准防抖的流程,那就是在一定的时间内只执行一次,如果此时重复触发,则重新开始计时。代码改进之后展示

function myRef(value) {return customRef((track, trigger) => {let timer    // 定义变量,接收定时器return {get() {track();return value;},set(newValue) {clearTimeout(timer);    // 每次开启定时器之前先清除之前的定时器,防止出现错误timer = setTimeout(() => {value = newValue;trigger();}, 500);},};});
}

连续快速点击效果:只有在最后一次点击完成,且定时器延迟触发之后,才会展示改变后的值

慢速点击效果:每次点击都等待定时器执行完毕之后再触发下一次动作

到了这里,其实我们就完成了对依赖项跟踪和更新触发进行显式控制。可以看到,track() 应该在 get() 方法中调用,而 trigger() 应该在 set() 中调用。但是其实我们完全实控制了 track()、trigger() 的使用,包括但不限于在哪使用,是否需要使用等。

问题点

当你将 customRef 作为 prop 传递时,它可能会影响父组件和子组件之间的关系,尤其是在响应式系统的依赖追踪和更新通知方面。

案例代码

// 自定义 ref,没有调用 track()
function useCustomRef(value) {return customRef((track, trigger) => ({get() {track()return value;},set(newValue) {value = newValue;trigger(); // 触发更新}}));
}// 父组件
export default {setup() {const customValue = useCustomRef('Hello');return { customValue };},template: '<ChildComponent :propValue="customValue" />'
};// 子组件
export default {props: {propValue: {type: Object,required: true}},watch: {propValue(newValue) {console.log('Prop value updated:', newValue);}},template: '<div>{{ propValue }}</div>'
};

1. 依赖追踪不完整

在Vue 响应式系统中 ,Vue会自动进行依赖追踪。当父组件传递一个 ref 或响应式对象作为 prop 给子组件时,Vue 会追踪这个 prop 的依赖。

但是,customRef 可以自定义依赖追踪逻辑。如果你在 customRefget 方法中没有正确调用 track(),Vue 就无法知道子组件在依赖这个 prop。这意味着,当父组件更新这个 prop 时,子组件可能无法感知到这个变化,因为依赖关系没有被正确建立。

2. 更新通知的不一致

当你在 customRefset 方法中没有正确调用 trigger(),即使 prop 在父组件中被更新,子组件也不会收到更新通知。这会导致子组件的数据与父组件不同步,从而产生 UI 不一致的问题。

3. 异步逻辑导致的延迟

如果 customRef 中包含异步逻辑(例如防抖或节流),这种延迟处理可能会导致子组件在接收 prop 时得到的是过时的数据。这在需要子组件立即响应父组件更新的场景下,可能引发状态不同步的问题。

在上面的例子中,debouncedRef 可能导致子组件在 prop 变更后并未立即更新,而是延迟更新,可能引发父子组件数据状态不同步的问题。

总结

1、customRef的作用: 创建一个自定义的 ref 函数,在其内部显式声明对其依赖追踪和更新触发的控制方式。

2、customRef 接收的工厂函数接收  track 和 trigger 两个函数作为参数,这两个函数其实就是对应的 ref() 中的 trackRefValue() 和 triggerRefValue() ,并返回一个带有 get 和 set 方法的对象。

3、一般来说,track() 应该在 get() 方法中调用,而 trigger() 应该在 set() 中调用。然而事实上,你对何时调用、是否应该调用他们有完全的控制权。

4、当 customRef 作为 prop 传递时,可能会影响父组件和子组件之间的关系,

  • 依赖追踪不完整
  • 更新通知的不一致
  • 异步逻辑导致的延迟

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

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

相关文章

微气象在线监测系统:宏观层面的电网灾害预防和应急管理

微气象受局部地形&#xff08;如山谷、河谷&#xff09;、地物&#xff08;如建筑物、森林&#xff09;和地面条件&#xff08;如水面、农田&#xff09;的影响较大&#xff0c;而大范围气象环境则更多地受气候系统和天气模式的控制。输电线路微气象监测的主要目的是为了评估和…

YOLOv8环境搭建、创建数据集、训练推理教程(超级详细)

yolov8和yolov10 是一个流派&#xff0c;和yolov5区别还挺大&#xff0c;所以尝试使用yolov8来进行模型训练&#xff0c;下面是详细使用流程&#xff1a; 一、环境搭建 1.1 Anaconda安装 Anaconda是一个强大的开源数据科学平台,它将很多好的工具整合在一起&#xff0c;极大地…

华为海思招聘-芯片与器件设计工程师-数字芯片方向- 机试题——(共九套)(每套四十题)

华为海思招聘-芯片与器件设计工程师-数字芯片方向- 机试题-题目分享——共九套&#xff08;每套四十题&#xff09; 岗位——芯片与器件设计工程师 岗位意向——数字芯片 真题题目分享&#xff0c;完整版带答案(有答案和解析&#xff0c;答案非官方&#xff0c;未仔细校正&am…

论文阅读:VideoMamba: State Space Model for Efficient Video Understanding

论文地址&#xff1a;arxiv 摘要 为了解决视频理解中的局部冗余与全局依赖性的双重挑战。作者将 Mamba 模型应用于视频领域。所提出的 VideoMamba 克服了现有的 3D 卷积神经网络与视频 Transformer 的局限性。 经过广泛的评估提示了 VideoMamba 的能力&#xff1a; 在视觉领…

Hbuilder创建的项目(uniApp + Vue3)中引入UnoCSS原子css引擎

这里是UnoCSS的官网介绍 UnoCS通过简化和优化CSS的编写过程来提高Web开发的效率和可维护性。好处是&#xff1a; 提升开发效率提升开发效率提高一致性增强灵活性易于维护方便的集成与配置 同时还支持预设变量和规则。这些可参看官网进行配置。Unocss通过其原子化方法、高度的…

第二证券:静态市盈率与动态市盈率有什么区别?

市盈率&#xff08;PE&#xff09;&#xff0c;是指投资者愿意为每一元净利润所支付的价格。 股票的市盈率股票价格&#xff08;P&#xff09;/每股净利润&#xff08;EPS&#xff09;&#xff0c;或者用公司其时总市值/公司上一年总净利润。 动态市盈率与静态市盈率的区别&a…

<数据集>遥感航拍飞机和船舶和识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;19973张 标注数量(xml文件个数)&#xff1a;19973 标注数量(txt文件个数)&#xff1a;19973 标注类别数&#xff1a;2 标注类别名称&#xff1a;[ship,plane] 序号类别名称图片数框数1ship17575416292plane239815…

对比 PDAF、CDAF 和 LAAF 自动对焦技术

深入解析相位检测自动对焦&#xff08;PDAF&#xff09; 相位检测自动对焦&#xff08;PDAF&#xff0c;Phase Detection Auto Focus&#xff09;是一种高效的自动对焦技术&#xff0c;广泛应用于现代数码相机、无反相机和智能手机摄像头中。为了更好地理解 PDAF&#xff0c;我…

基于协同过滤算法的电影推荐系统的设计与实现(论文+源码)_kaic

摘 要 现在观看电影已逐渐成为人们日常生活中最常见的一种娱乐方式&#xff0c;人们通常会在周末或在休息、吃饭时间不由自主地在各种视频软件中搜索当前火热的影视节目。但是现在的视频软件电影推荐功能不够完善&#xff0c;所以需要开发出一套系统来使用户只需要简单操作就能…

华为云征文|部署私有云和文档管理系统 Kodcloud

华为云征文&#xff5c;部署私有云和文档管理系统 Kodcloud 一、Flexus云服务器X实例介绍1.1 云服务器介绍1.2 应用场景1.3 对比普通ECS 二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 Kodcloud3.1 Jellyfin 介绍3.2 Docker 环境搭建3.3 Jell…

【智能算法改进】路径规划问题的多策略改进樽海鞘群算法研究

目录 1.算法原理2.改进点3.结果展示4.参考文献5.代码获取 1.算法原理 【智能算法】樽海鞘群算法&#xff08;SSA)原理及实现 2.改进点 无标度网络策略 复杂网络在图论中可以用边和节点表示&#xff0c; Barabasi 等于1999年通过分析大量的数据提出了无标度网络模型. 该网络…

人像比对-人证比对-人脸身份证比对-人脸身份证实名认证-人脸三要素对比-实人认证

​ 人证比对API接口&#xff0c;全称为人脸身份证比对API接口&#xff0c;也被称为人脸实名认证API接口或实人认证API接口。这种接口服务主要用于将提供的人脸图片和对应的身份证照片、姓名、身份证号码进行比对&#xff0c;以此验证用户的身份。以下是关于人证比对API接口的详…

[易聊]软件项目测试报告

一、项目背景 随着互联网发展&#xff0c;各种各样的软件&#xff0c;比如游戏、短视频、购物软件中都有好友聊天功能&#xff0c;这是一个可在浏览器中与好友进行实时聊天的网页程序。“ 易聊 ”相对于一般的聊天软件&#xff0c;可以让用户免安装、随时随地的通过浏览器网页…

UDP英译汉网络词典

这里我们用UDP实现一个简单的英译汉小词典。我们还是仿照前一篇的UDP编程&#xff0c;将各自的组件封装起来&#xff0c;实现高内聚低耦合。 一. 字典翻译功能实现 首先我们将我们的字典知识库放在txt文本中。 apple: 苹果 banana: 香蕉 cat: 猫 dog: 狗 book: 书 pen: 笔 ha…

浮毛粘毛器可以彻底去除吗?独家揭秘值得入手浮毛空气净化器

有没有养猫五年以上还是单猫的铲屎官&#xff1f;能不能分享一下怎么才能控制住不养新猫。 从我养第一只猫开始&#xff0c;每次看到别人家的小幼猫&#xff0c;就控制不住的想养。到现在&#xff0c;家里已经有了7只猫&#xff0c;而前段时间楼下那只小三花又差点让我破例。不…

Keil5 Debug模式Watch窗口添加的监控变量被自动清除

Keil5 Debug模式Watch窗口添加的监控变量被自动清除 问题解决记录 问题描述&#xff1a;每次进入Debug模式时&#xff0c;watch窗口里面上一次调试添加的监控变量都会被全部清掉 如图&#xff1a; 退出Debug模式后&#xff0c;重新进入Debug模式&#xff1a; 解决方法&…

INFO:一种基于向量加权平均的高效优化算法【免费获取Matlab代码】

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2022年&#xff0c;I Ahmadianfar受到基于向量加权平均方法启发&#xff0c;提出了加权平均向量优化算法&#xff08;weIghted meaN oF vectOrs, INFO&#xff09;。 2.算法原理 2.1算法…

在线任务分发系统开发架构分析

在线任务分发系统的架构分析是一个综合性的过程&#xff0c;涉及多个技术层面和功能模块的设计。以下是对在线任务分发系统架构的详细分析&#xff1a; 一、系统概述 在线任务分发系统是一个集任务发布、分配、执行、监控及反馈于一体的综合平台&#xff0c;它通过互联网技术实…

超详细带你学习go高性能web框架----fiber

go-fiber-fast go-fiber 主要定位为一个轻量级、高性能的 Web 框架&#xff0c;但其灵活性使得它可以通过与其他库的集成&#xff0c;构建出强大而多功能的应用程序&#xff0c;满足不同的业务需求,和gin一样轻量级别的路由,但是性能特别是极端性能比gin好一些,都可以通过整合其…

【Gradle】window下安装gradle及idea配置

gradle安装与配置 背景基本概念下载配置环境变量idea配置构建命令配置全局的镜像仓库 背景 最近在看spring源码时&#xff0c;Spring5 以后都是采用 Gradle 来编译&#xff0c;所以构建源码前先安装 Gradle 环境。 基本概念 Gradle是一个基于Apache Ant和Apache Maven概念的…