ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

VueUse extendRef 深度解析:为 Ref 扩展附加属性与响应式能力

VueUse extendRef 深度解析:为 Ref 扩展附加属性与响应式能力 前端【免费下载链接】vueuseCollection of essential Vue Composition Utilities for Vue 3项目地址https://gitcode.com/gh_mirrors/vu/vueuse点击查看免费下载extendRef是 VueUsepackages/shared/extendRef中一个精巧的 Reactivity 工具函数它允许你在不修改原有 Ref 行为的前提下为 Ref 对象动态附加自定义属性并且这些附加属性在传入Ref值时会自动解包、保持响应式联动。阅读本文后你将掌握extendRef的完整用法、两个核心配置项enumerable与unwrap的底层语义以及它是如何成为refWithControl等高级响应式工具的地基。功能定位给 Ref 添加额外属性在 Vue 3 中Ref本质上是一个带有value属性、并且value具备响应式能力的对象。但很多场景下我们希望一个 ref 除了携带数据之外还能顺带携带一些元数据或配套方法——例如给ref挂上一个get/set函数、一份说明文字或关联的辅助对象。extendRef(ref, extend, options?)正是为此而生它以第一个参数ref为基底把第二个参数extend对象上的所有属性合并进这个 ref 本身并返回同一个 ref 对象ref的引用没有被替换只是原地扩展。快速上手最简单的扩展用法原文档给出了最直观的使用示例——先构造一个shallowRef再用extendRef挂上一个普通字符串属性import { extendRef } from vueuse/core import { shallowRef } from vue const myRef shallowRef(content) const extended extendRef(myRef, { foo: extra data }) extended.value content extended.foo extra data执行完成后extended仍然是原来的那个 refextended.value照常读取/写入原始内容同时extended.foo可以像普通对象属性一样被访问。需要注意的关键约束原文档明确强调附加的属性在 Vue 的模板中是不可访问的。原因在于 Vue 模板对 ref 的解包只针对value这一约定属性extendRef附加的其他键并不会参与模板编译期的解包逻辑。因此这类扩展属性更适合在组合式函数内部、watch/computed/普通 JS 逻辑中消费而不是直接暴露给模板。附加属性传入 Ref自动解包与双向响应式extendRef更强大的能力在于当extend对象中的某个属性值本身是一个Ref时它会把这个属性解包为普通值并保持与源 ref 的双向响应式同步。原文档示例import { extendRef } from vueuse/core import { shallowRef } from vue const myRef shallowRef(content) const extraRef shallowRef(extra) const extended extendRef(myRef, { extra: extraRef }) extended.value content extended.extra extra extended.extra new data // 赋值会触发更新 extraRef.value new data这里extended.extra读到的是extraRef.valueextra 而非 ref 对象本身而当你给extended.extra赋值时值会被写回extraRef.value从而触发依赖该 ref 的响应式更新。也就是说扩展出的属性与源 ref 是双向联动的这为实现带控制的 ref见下文refWithControl提供了基础机制。配置项详解enumerable与unwrapextendRef接受一个可选的第三个参数其类型定义位于源码的 ExtendRefOptions 接口export interface ExtendRefOptionsUnwrap extends boolean boolean { /** * 扩展属性是否可枚举 * * default false */ enumerable?: boolean /** * 是否对 Ref 类型的属性值进行解包 * * default true */ unwrap?: Unwrap }选项默认值作用enumerablefalse控制扩展属性是否出现在Object.keys、for...in、展开运算符等枚举操作中。默认不可枚举避免污染对 ref 的常规遍历unwraptrue控制传入extend的Ref值是否被解包为value。默认解包并建立 getter/setter 双向同步设为false则原样挂载 ref 对象本身源码级原理Object.defineProperty的两次分派从源码结构看extendRef的实现非常克制核心逻辑只有一段循环见 extendRef 实现export function extendRefR extends Refany, Extend extends object( ref: R, extend: Extend, { enumerable false, unwrap true }: ExtendRefOptions {}, ): ExtendRefReturnUnwrapRefR { for (const [key, value] of Object.entries(extend)) { if (key value) continue if (isRef(value) unwrap) { Object.defineProperty(ref, key, { get() { return value.value }, set(v) { value.value v }, enumerable, }) } else { Object.defineProperty(ref, key, { value, enumerable }) } } return ref }可以提炼出三个关键设计跳过value键if (key value) continue是一道安全闸门——value是 ref 的核心访问器extendRef坚决不碰它避免覆盖 Vue 的响应式语义。Ref 值走 getter/setter 分支当属性值是 ref 且unwrap为true时通过Object.defineProperty定义一对访问器get()读取value.valueset(v)写入value.value。由于写入的是源 ref 的valueVue 的依赖收集与派发更新机制自然被触发——这就是赋值会触发更新的底层来源。普通值走数据属性分支非 ref 属性或unwrap: false时的 ref则直接以数据属性挂载enumerable决定其是否可被枚举。最终函数返回ref本身原地扩展因此返回值在isRef检查下依然成立完全兼容原有 ref 的一切用法。类型层面的两套重载为了让 TS 用户获得精确的类型推断extendRef声明了两套函数重载见 源码重载声明// Overload 1unwrap 显式设为 false export function extendRef R extends Refany, Extend extends object, Options extends ExtendRefOptionsfalse, (ref: R, extend: Extend, options?: Options): ShallowUnwrapRefExtend R // Overload 2unwrap 未设置或设为 true export function extendRef R extends Refany, Extend extends object, Options extends ExtendRefOptions, (ref: R, extend: Extend, options?: Options): Extend Runwrap: false时返回类型为ShallowUnwrapRefExtend R附加属性保持浅解包的 ref 形态便于你显式访问 ref 对象。unwrap为true默认时返回类型为Extend R附加属性直接表现为普通值类型与运行时解包行为保持一致。两个重载的交叉类型都包含R从类型层面保证了返回值仍是原来的 ref。实战印证refWithControl如何消费extendRefextendRef并非孤立存在它是 VueUse 内部多个高级工具的实现地基。最典型的例子是 refWithControl源码 ——它用customRef构造一个可精细控制跟踪/触发的 ref然后借助extendRef把get、set、untrackedGet、silentSet、peek、lay六个控制方法挂到 ref 上return extendRef( ref, { get, set, untrackedGet, silentSet, peek, lay, }, { enumerable: true }, )注意这里显式传入了{ enumerable: true }因为refWithControl期望这些方法对使用者可见、可枚举。其配套文档 refWithControl 使用说明 展示了典型场景import { refWithControl } from vueuse/core const num refWithControl(0) const doubled computed(() num.value * 2) num.value 42 console.log(num.value) // 42 console.log(doubled.value) // 84 // 不触发响应式更新的赋值 num.set(30, false) console.log(num.value) // 30 console.log(doubled.value) // 84未更新refWithControl的测试用例index.test.ts也从侧面验证了extendRef挂载属性的行为符合预期expect(isRef(ref)).toBe(true)确认扩展后的对象仍是 refref.lay(42)、ref.silentSet(42)之后ref.value变为 42 但watch回调不执行证明扩展出的set系列方法确实绕过了触发机制onBeforeChange返回false可以驳回变更等等。这说明extendRef定义在 ref 上的扩展属性与 Vue 的响应式体系完全兼容、可被正常测试与依赖。使用注意与适用边界综合原文档与源码使用extendRef时有几点值得留意模板不可访问扩展属性只存在于 ref 对象上模板解包机制不会识别它们请在组合式函数/JS 逻辑中消费。value键被保留extend对象中名为value的键会被静默跳过无法用来覆盖 ref 的核心值。原地扩展、返回原对象函数不创建新 ref返回的是传入的同一个 ref因此extendRef前捕获的引用也能看到扩展后的属性。enumerable默认关闭如果不显式开启扩展属性不会出现在Object.keys/ 展开运算中refWithControl这类需要暴露方法的场景则应显式传{ enumerable: true }。导入路径extendRef通过 packages/shared/index.ts 统一导出可从vueuse/core直接引入。如果你需要的是既能控制何时跟踪/触发响应式、又暴露精细方法的 ref直接使用基于extendRef构建的refWithControl会更省力而当你只想为某个自定义 ref 轻量挂载元数据或辅助函数时extendRef本身就是一个足够干净、低侵入的解决方案。赞分享前端【免费下载链接】vueuseCollection of essential Vue Composition Utilities for Vue 3项目地址https://gitcode.com/gh_mirrors/vu/vueuse点击查看免费下载相关推荐VueUse useCloned 深入解析为 Ref 打造响应式克隆副本的完整实战指南VueUse useCloned 深入解析为 Ref 打造响应式克隆副本的完整实战指南 useCloned 是 VueUse 核心包中一个轻量但极为实用的工具前端5 步部署 AzerothCore魔兽世界私服从零跑起来5 步部署 AzerothCore魔兽世界私服从零跑起来 一个周末把一台 3.3.5 的魔兽世界私服拉起来不用买服务器、不用手搓环境AzerothCor游戏开发后端VueUse Electron 扩展包用 Composition API 响应式封装 Electron 渲染进程能力VueUse Electron 扩展包用 Composition API 响应式封装 Electron 渲染进程能力 vueuse/electron 是 V前端上一篇EmotiVoice易魔声2000音色免费开源TTS引擎5分钟快速上手指南下一篇易魔声EmotiVoice终极指南2000音色免费开源5分钟解决语音合成三大痛点创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表