- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
createUnrefFn是 VueUse(Vue 3 组合式工具集)packages/core 中一个精巧的实用函数:它把一个普通函数包装成"参数会自动解包 ref"的新函数,返回值类型与原始函数完全一致。在需要把shallowRef、ref等响应式数据直接传入非响应式 API(如fetch、JSON.stringify、第三方库回调)时,用它可以免去手动toValue/unref的样板代码。读完本文你将掌握createUnrefFn的用法、类型推导原理、与reactify的取舍,以及它在 VueUse 源码中的真实实现。
一、为什么需要 createUnrefFn
在 Vue 3 中,组合式函数通常接收ref作为参数并返回响应式结果。但很多原生 API(fetch、navigator.clipboard、WebSocket等)和第三方函数并不认识Ref对象——直接传入会得到"[object Object]"之类的错误结果,甚至直接抛错。
传统做法是手动在每个调用点解包:
const url = shallowRef('https://httpbin.org/post') // 每次都要手动 .value / toValue fetch(toValue(url), { ... })当函数有多个 ref 参数、且在多处调用时,手动解包既啰嗦又容易遗漏。createUnrefFn把"解包"集中到一次包装中,之后所有调用都自动完成。
二、用法与完整示例
原文档给出的典型场景是包装一个post请求函数(见 index.md):
import { createUnrefFn } from '@vueuse/core' import { shallowRef } from 'vue' const url = shallowRef('https://httpbin.org/post') const data = shallowRef({ foo: 'bar' }) function post(url, data) { return fetch(url, { data }) } const unrefPost = createUnrefFn(post) post(url, data) // ❌ 参数是 ref,fetch 无法识别,会抛错 unrefPost(url, data) // ✔️ 参数自动 unref 后传入,正常工作要点:
- 原函数
post原样保留,未被修改; unrefPost与post的返回值完全相同(fetch返回的Promise),类型也被完整保留;- 你依然可以向包装后的函数传入原始值(非 ref),
createUnrefFn对原始值不做任何处理,直接透传。
也可以用于任何普通函数,例如格式化字符串:
import { createUnrefFn } from '@vueuse/core' import { shallowRef } from 'vue' const name = shallowRef('VueUse') const format = (name: string) => `Hello, ${name}!` const unrefFormat = createUnrefFn(format) console.log(unrefFormat(name)) // Hello, VueUse!安装与引入
createUnrefFn属于@vueuse/core包。安装 VueUse 后直接命名导入即可(入口见 packages/core/index.ts):
pnpm add @vueuse/core # 或 npm install @vueuse/core / yarn add @vueuse/coreimport { createUnrefFn } from '@vueuse/core'从 package.json 可以看到,@vueuse/core要求 Vue^3.5.0作为 peer dependency,并提供dist/index.js等构建产物;源码中声明了@__NO_SIDE_EFFECTS__,配合"sideEffects": false,支持 tree-shaking,未使用时不进入最终 bundle。
三、源码实现:一行核心逻辑
createUnrefFn的实现非常精简,完整源码在 packages/core/createUnrefFn/index.ts:
import type { MaybeRef } from 'vue' import { toValue } from 'vue' export type UnrefFn<T> = T extends (...args: infer A) => infer R ? (...args: { [K in keyof A]: MaybeRef<A[K]> }) => R : never /** * Make a plain function accepting ref and raw values as arguments. * Returns the same value the unconverted function returns, with proper typing. * * @__NO_SIDE_EFFECTS__ */ export function createUnrefFn<T extends Function>(fn: T): UnrefFn<T> { return function (this: any, ...args: any[]) { return fn.apply(this, args.map(i => toValue(i))) } as UnrefFn<T> }关键点拆解:
toValue是核心:Vue 3.3+ 提供的toValue对ref/getter/普通值一视同仁,统一取出其当前值。这里逐个参数args.map(i => toValue(i)),因此任意位置的 ref 参数都会被自动解包,非 ref 参数原样透传。this透传:包装函数使用fn.apply(this, ...),如果原函数依赖this上下文(如对象方法),调用方式不变依然生效。@__NO_SIDE_EFFECTS__:向打包器标注该函数无副作用,配合 tree-shaking 可安全摇树。- 实现来源:文件头部注明,该实现移植自 Stanley Horwood 的
pompaute库。
测试验证
仓库中的单元测试 index.browser.test.ts 覆盖了两个核心契约:
it('should return a function that returns the same value', () => { const value = 42 const fn = (value: any) => value const res = fn(value) const resWrapped = createUnrefFn(fn)(value) expect(res).toBe(resWrapped) })- 包装后的函数返回值与原始函数严格相等(
toBe); - 同时验证了传入原始值(非 ref)时也能正常工作。
四、类型推导:UnrefFn 是如何工作的
createUnrefFn的泛型签名保证了"参数接受 ref、返回值不变"的类型安全性:
export type UnrefFn<T> = T extends (...args: infer A) => infer R ? (...args: { [K in keyof A]: MaybeRef<A[K]> }) => R : never- 通过条件类型提取原函数的参数元组
A和返回值类型R; - 用映射类型
{ [K in keyof A]: MaybeRef<A[K]> }把每个参数类型包一层MaybeRef<T>(即T | Ref<T>),于是新函数既接受Ref<T>也接受原始T; - 返回值类型
R原样保留——例如包装fetch,返回类型依然是Promise<Response>,不会变成ComputedRef。
因此与reactify(返回ComputedRef)不同,createUnrefFn是一次性求值、返回值不变的包装,而非响应式转换。
五、createUnrefFn 与 reactify 如何选择
原文档明确给出提示(index.md 的 tip 区块):请确认自己在用对工具。两者对比:
| 维度 | createUnrefFn | reactify |
|---|---|---|
| 返回值 | 原函数的返回值(如Promise、普通值) | ComputedRef<R>,依赖变化时自动重算 |
| 求值时机 | 每次调用时求值一次 | 响应式追踪,参数变化自动重新求值 |
| 适用场景 | 一次性调用、不关心响应式更新 | 需要"参数变化 → 结果自动更新" |
例如把Math.pow变成响应式版本:
import { reactify } from '@vueuse/core' import { shallowRef } from 'vue' const pow = reactify(Math.pow) // 返回 ComputedRef const a = shallowRef(3) const b = shallowRef(4) const c = reactify((a: number, b: number) => a + b)reactify的源码(packages/shared/reactify/index.ts)能看出根本差异:它内部把参数交给computed(() => fn.apply(this, args.map(i => toValue(i)))),即每次求值都发生在 computed 的响应式上下文中,因此 ref 参数变化时结果自动更新;而createUnrefFn只是调用时解包一次,不建立任何响应式依赖。
选择建议:
- 只要"当前值调用一次",或返回值本就不是普通值(如
fetch返回Promise)、函数是副作用型 API 调用 → 用createUnrefFn; - 需要"响应式输入、自动更新的计算结果"(纯函数、数学运算、字符串处理)→ 用
reactify。
六、源码中的佐证与体积信息
- 导出入口:packages/core/index.ts 第 5 行
export * from './createUnrefFn',从@vueuse/core可直接导入; - 实现与文档目录:packages/core/createUnrefFn(含
index.ts、index.md、index.browser.test.ts); - 体积:
export-size.json记录createUnrefFn压缩后约100 B(见 packages/export-size.json),是一个非常轻量的工具函数; - 关联概念:
reactify(别名createReactiveFn)位于 packages/shared/reactify,是文档中 recommended 的姊妹函数。
七、小结
createUnrefFn用极少的代码解决了真实痛点:把"参数是 ref 的普通函数"一键变成可安全调用、类型完备的函数。它不引入响应式系统、不改变返回值、不修改原函数,配合toValue与精心的泛型推导,成为 VueUse 中处理"ref 与原生 API 对接"的最简方案。需要响应式自动求值时,则切换到reactify——两者互补,构成了 VueUse 中"解包 ref 参数"的完整工具链。
- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
相关推荐
VueUse createUnrefFn 深入指南:让普通函数无缝接收 Ref 与原始值参数
VueUse createUnrefFn 深入指南:让普通函数无缝接收 Ref 与原始值参数 本文基于本仓库 .agents 技能集中 VueUse 函数参考文
AI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染MikroORM 实体构造函数完全指南:`em.create()` 参数推断、`rel()`/`ref()` 引用与 `forceEntityConstructor` 详解
MikroORM 实体构造函数完全指南: em.create 参数推断、 rel / ref 引用与 forceEntityConstructor 详解 本文基
后端Sway 函数全解析:从 fn 声明到 ref mut 可变参数
Sway 函数全解析:从 fn 声明到 ref mut 可变参数 本指南以 Sway 智能合约语言(Fuel 生态)官方 Book 的 Functions 章节
编程语言编译器区块链
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考