☰
VueUse createUnrefFn 完全指南:让普通函数自动解包 Ref 参数
2026/10/1 21:26:05 网站建设 项目流程
  • 前端

【免费下载链接】vueuse

Collection of essential Vue Composition Utilities for Vue 3

项目地址:https://gitcode.com/gh_mirrors/vu/vueuse
点击查看免费下载

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/core
import { 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> }

关键点拆解:

  1. toValue是核心:Vue 3.3+ 提供的toValue对ref/getter/普通值一视同仁,统一取出其当前值。这里逐个参数args.map(i => toValue(i)),因此任意位置的 ref 参数都会被自动解包,非 ref 参数原样透传。
  2. this透传:包装函数使用fn.apply(this, ...),如果原函数依赖this上下文(如对象方法),调用方式不变依然生效。
  3. @__NO_SIDE_EFFECTS__:向打包器标注该函数无副作用,配合 tree-shaking 可安全摇树。
  4. 实现来源:文件头部注明,该实现移植自 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 区块):请确认自己在用对工具。两者对比:

维度createUnrefFnreactify
返回值原函数的返回值(如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

项目地址:https://gitcode.com/gh_mirrors/vu/vueuse
点击查看免费下载

相关推荐

上一篇:TanStack Table Angular 的 flexRenderComponent 函数:类型安全的表格单元格组件渲染指南
下一篇:Haversine 距离计算实战:在 Swift 中用 haversine 公式求球面两点距离(Swift Algorithm Club 实现解析)

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询