首页 / Nuxt 4 入门教程 / 组合式函数 composables

Nuxt 4 入门教程

组合式函数 composables

本教程共 50 篇 · 第 36 篇 · 更新于 2026-08-08 · 约 7 分钟阅读

NuxtNuxt4composables组合式函数自动导入

本节目标:理解 Nuxt 的 composables 目录约定,能把一段通用逻辑封装成组合式函数,并在全站任意地方免导入直接使用。

36-1

写 Vue 时,你一定遇到过这种情况:好几个页面都要「监听鼠标位置」「格式化日期」「判断用户是否登录」。如果每段逻辑都复制粘贴一遍,代码又臭又长,改一处要改五处。

组合式函数(composable)就是用来解决这个问题的。它本质上是一个返回响应式数据的普通函数,把「状态 + 逻辑」打包在一起,谁想用谁就调用它。在 Nuxt 里,只要把这类函数放进 app/composables/ 目录,框架就会自动扫描、自动导入,你在组件里直接写 useXxx() 就行,不用 import。

Note

命名约定:组合式函数一律以 use 开头,比如 useMouseuseDateFormat。这是 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 的组合式函数(如 useRouteuseRuntimeConfiguseFetch)大多依赖当前的「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 提供的 useFetchuseAsyncData 本身就是组合式函数,你可以直接套用:

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 }。这里有个容易踩的点:从对象里解构出 xy 这些属性时,它们的响应性不会丢,因为 ref 仍然在对象内部;但如果直接解构一个 ref 本身(比如 const { value } = someRef),拿到的就是普通值,不再是响应式的。所以组合式函数里多值一律用对象返回最稳。

Tip

两个组合式函数取了同名(比如都导出 useUser)会直接构建报错。命名时加上业务前缀(如 useUserProfileuseUserCart)能避开冲突,也更易读。
组合式函数里也常用 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 开头(如 useCounteruseAuth),让人一眼看出它是组合式函数;保持职责单一,一个函数解决一个问题;返回值用对象形式,方便调用方按需解构。

在 Nuxt 项目里,composables/ 目录下的函数会被自动导入。这意味着你在任何组件或其他组合式函数里都可以直接使用,不需要手动 import。但要注意,自动导入的前提是函数名和文件名一致(或文件里只有一个导出的函数)。

编写组合式函数时,如果内部用到了响应式数据,记得用 useState 而不是普通的 ref。这确保了在服务端渲染时,状态不会在不同请求间泄漏。这是 Nuxt 组合式函数和普通 Vue 组合式函数最重要的区别。

36-8 组合式函数与 SSR 的兼容

编写在 Nuxt 中使用的组合式函数时,必须考虑服务端渲染的兼容性。服务端没有 windowdocumentnavigator 等浏览器 API。如果你的组合式函数里直接访问了这些对象,在服务端渲染时就会报错。

解决方法有几种。最简单的是用 import.meta.client 条件判断,只在客户端执行浏览器相关的代码。更优雅的方式是把浏览器 API 的访问放到 onMounted 钩子里,因为 onMounted 只在客户端执行。

另外,如果组合式函数需要维护状态,使用 Nuxt 提供的 useState 而不是 Vue 的 refuseState 是 SSR 安全的,它确保每个请求有独立的状态副本。这是 Nuxt 组合式函数和纯 Vue 组合式函数最重要的区别之一。