组合式函数 composables
本教程共 50 篇 · 第 36 篇 · 更新于 2026-08-08 · 约 7 分钟阅读
本节目标:理解 Nuxt 的 composables 目录约定,能把一段通用逻辑封装成组合式函数,并在全站任意地方免导入直接使用。
36-1
写 Vue 时,你一定遇到过这种情况:好几个页面都要「监听鼠标位置」「格式化日期」「判断用户是否登录」。如果每段逻辑都复制粘贴一遍,代码又臭又长,改一处要改五处。
组合式函数(composable)就是用来解决这个问题的。它本质上是一个返回响应式数据的普通函数,把「状态 + 逻辑」打包在一起,谁想用谁就调用它。在 Nuxt 里,只要把这类函数放进 app/composables/ 目录,框架就会自动扫描、自动导入,你在组件里直接写 useXxx() 就行,不用 import。
Note命名约定:组合式函数一律以
use开头,比如useMouse、useDateFormat。这是 Vue 生态的通行习惯,Nuxt 也按这个规则识别并自动导入。
36-2
假设我们想做一个「鼠标位置」的组合式函数。在 app/composables/useMouse.ts 里写:
export const useMouse = () => {
const x = ref(0)
const y = ref(0)
const update = (event: MouseEvent) => {
x.value = event.clientX
y.value = event.clientY
}
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
return { x, y }
}
然后在任意页面直接用,完全不需要 import:
<script setup lang="ts">
const { x, y } = useMouse()
</script>
<template>
<p>鼠标位置:{{ x }}, {{ y }}</p>
</template>
注意看:页面里没有 import { useMouse },但函数照样能用。这就是自动导入(auto-imports)的威力。
Tip组合式函数返回的是
ref,在模板里直接用{{ x }}就能取到值,不需要写x.value。只有在<script>里操作它时才需要.value。
36-3
有个坑必须提前讲清楚:Nuxt 的组合式函数(如 useRoute、useRuntimeConfig、useFetch)大多依赖当前的「Nuxt 上下文」。你得在组件、页面、插件的 setup 里同步调用它们,不能随便在模块顶层、定时器回调里乱用。
下面这样写是错的:
// 错误:在组合式函数之外访问运行时配置
const config = useRuntimeConfig()
export const useMyComposable = () => {
// ...
}
正确写法是把调用放进函数体内:
export const useMyComposable = () => {
// 因为组合式函数在组件生命周期内被调用,这里可以安全使用
const config = useRuntimeConfig()
return { config }
}
Warning如果你看到报错
Nuxt instance is unavailable,八成是把 Nuxt 组合式函数写到了生命周期之外(比如模块顶层、普通函数体内、或await之后的回调里)。记住一条铁律:调用组合式函数的地方,必须还在 Nuxt 上下文里。
36-4
组合式函数里经常要发请求。Nuxt 提供的 useFetch、useAsyncData 本身就是组合式函数,你可以直接套用:
export const useUser = () => {
const { data: user, status } = useFetch('/api/me', {
key: 'current-user',
})
return { user, status }
}
它返回 user 这个 ref,组件里直接用 user.value 读取,还能天然享受 SSR 数据复用(服务端取过,客户端不再重复取)。
36-5
Nuxt 还约定了一个 app/utils/ 目录,用来放「不需要响应式、纯工具」的函数,比如格式化、校验:
export const formatPrice = (value: number) => {
return '¥' + value.toFixed(2)
}
<script setup lang="ts">
const price = formatPrice(19.9)
</script>
utils/ 和 composables/ 都会自动导入,区别只在语义:composables 偏「带状态的逻辑」,utils 偏「纯函数」。你不必纠结,按团队习惯分就行。
36-6
有些库(比如 vue-i18n)也提供了组合式函数,你想免导入就用,可以在 nuxt.config.ts 里登记:
export default defineNuxtConfig({
imports: {
presets: [
{
from: 'vue-i18n',
imports: ['useI18n'],
},
],
},
})
这样 useI18n 也能像内置函数一样直接用了。
Tip想显式导入也行:用
#imports别名即可,例如import { ref, useMouse } from '#imports'。这在你想关掉自动导入、或写测试时特别有用。
36-7
Nuxt 3 的组合式函数目录在项目根 composables/,Nuxt 4 改到 app/composables/(因为 app/ 成了新的源码根)。函数写法、自动导入机制完全一致,老项目不强制迁移。
36-8
Nuxt 启动时会递归扫描 app/composables/ 下的所有 .ts/.js 文件,把其中导出的、以 use 开头的函数自动登记成全局可导入。子目录也会被扫到,比如 app/composables/user/useUser.ts 里的 useUser,你在页面里照样直接 useUser() 调用,不用管它藏在哪一层目录。扫描结果写进 .nuxt/imports.d.ts,IDE 的类型提示就来自这里。
返回值有两种常见风格。只返回一个值时,直接 return ref(...);返回多个值时,用对象包起来,比如 { x, y }。这里有个容易踩的点:从对象里解构出 x、y 这些属性时,它们的响应性不会丢,因为 ref 仍然在对象内部;但如果直接解构一个 ref 本身(比如 const { value } = someRef),拿到的就是普通值,不再是响应式的。所以组合式函数里多值一律用对象返回最稳。
Tip两个组合式函数取了同名(比如都导出
useUser)会直接构建报错。命名时加上业务前缀(如useUserProfile、useUserCart)能避开冲突,也更易读。
组合式函数里也常用watch监听响应式数据变化。它和你在普通组件里写的watch完全一致,Nuxt 不会改变它的行为。一个容易忽略的点:watch的回调默认是懒执行,只有数据变了才触发,组件刚创建时不会跑。想一开始就执行一次,记得加{ immediate: true }。
Tip把组合式函数写进
app/composables/后,如果 IDE 一直不给自动导入提示,别急着手改代码,先确认.nuxt/imports.d.ts已生成、开发服务器在跑。扫描是构建时做的,服务器没起来,声明文件就不会更新。
36-9
把可复用逻辑放进 app/composables/,命名为 useXxx,返回 ref 或对象,就能在全站免 import 使用。记得在 Nuxt 上下文里调用组合式函数,别在模块顶层乱用。下一章我们看比组合式函数「更重」一层的复用方式:插件。
36-7 组合式函数的设计原则
好的组合式函数应该像一个黑盒:接收明确的输入,返回可预测的输出,内部逻辑对外透明但不需要关心。设计时遵循几个原则:命名以 use 开头(如 useCounter、useAuth),让人一眼看出它是组合式函数;保持职责单一,一个函数解决一个问题;返回值用对象形式,方便调用方按需解构。
在 Nuxt 项目里,composables/ 目录下的函数会被自动导入。这意味着你在任何组件或其他组合式函数里都可以直接使用,不需要手动 import。但要注意,自动导入的前提是函数名和文件名一致(或文件里只有一个导出的函数)。
编写组合式函数时,如果内部用到了响应式数据,记得用 useState 而不是普通的 ref。这确保了在服务端渲染时,状态不会在不同请求间泄漏。这是 Nuxt 组合式函数和普通 Vue 组合式函数最重要的区别。
36-8 组合式函数与 SSR 的兼容
编写在 Nuxt 中使用的组合式函数时,必须考虑服务端渲染的兼容性。服务端没有 window、document、navigator 等浏览器 API。如果你的组合式函数里直接访问了这些对象,在服务端渲染时就会报错。
解决方法有几种。最简单的是用 import.meta.client 条件判断,只在客户端执行浏览器相关的代码。更优雅的方式是把浏览器 API 的访问放到 onMounted 钩子里,因为 onMounted 只在客户端执行。
另外,如果组合式函数需要维护状态,使用 Nuxt 提供的 useState 而不是 Vue 的 ref。useState 是 SSR 安全的,它确保每个请求有独立的状态副本。这是 Nuxt 组合式函数和纯 Vue 组合式函数最重要的区别之一。