自动导入
Nuxt 的自动导入是其核心特性之一,让你无需手动写 import 语句就能使用组件、组合式函数和工具。
为什么自动导入很重要?
| 没有 Nuxt 自动导入时 | 有 Nuxt 自动导入时 |
|---|---|
| 每个文件开头写 5-10 行 import | 不用写 import |
| 忘了 import 就报错 | 自动导入,不会忘 |
| 重命名组件要改所有 import | 改文件名就行,import 自动更新 |
| 代码中大量重复的 import 语句 | 代码更简洁 |
自动导入的本质
Nuxt 在构建时扫描约定目录,自动为你生成 import 语句。你在代码中"直接用",Nuxt 帮你"补 import"。
INFO
️ 注意:自动导入不是"魔法"——它只是帮你省略了 import 语句 代码运行时,import 仍然存在。如果你在 TypeScript 中看到类型报错,可能需要运行 nuxt prepare 重新生成类型声明
自动导入的目录
| 目录 | 导入类型 | 前缀规则 | 典型内容 |
|---|---|---|---|
app/components/ | Vue 组件 | 基于目录结构 | 页面组件、UI 组件 |
app/composables/ | 组合式函数 | use 前缀 | 状态管理、数据逻辑 |
app/utils/ | 工具函数 | 无前缀 | 格式化、计算、辅助函数 |
server/utils/ | 服务端工具 | 无前缀 | 数据库、加密、服务端逻辑 |
shared/utils/ | 共享工具 | 无前缀 | 前后端共用的纯函数 |
为什么 composables/ 要求 use 前缀?
这是 Vue 社区的命名约定:组合式函数以 use 开头(如 useCounter、useAuth)。Nuxt 借鉴了这个约定。虽然技术上不叫 useXxx 也能自动导入,但强烈建议遵守这个约定,代码可读性更好。
组件自动导入
app/components/ 下的 Vue 组件自动注册:
app/components/
├── AppHeader.vue → <AppHeader>
├── AppFooter.vue → <AppFooter>
├── Button.vue → <Button>
├── Card/
│ ├── Card.vue → <Card>
│ ├── CardHeader.vue → <CardHeader>
│ └── CardBody.vue → <CardBody>
└── user/
├── UserCard.vue → <UserCard>
└── UserAvatar.vue → <UserAvatar>在模板中直接使用:
<template>
<AppHeader />
<UserCard :user="user" />
<Card>
<CardHeader>标题</CardHeader>
<CardBody>内容</CardBody>
</Card>
</template>
<!-- 不需要 import AppHeader from './components/AppHeader.vue' -->
<!-- 不需要 import UserCard from './components/user/UserCard.vue' -->
<!-- 等等... -->以前用 Vue 是怎么做的?
<script setup>
import AppHeader from './components/AppHeader.vue'
import UserCard from './components/user/UserCard.vue'
import Card from './components/Card/Card.vue'
import CardHeader from './components/Card/CardHeader.vue'
import CardBody from './components/Card/CardBody.vue'
</script>Nuxt 帮你省掉了这些重复劳动。
懒加载组件
在组件名前加 Lazy 前缀即可懒加载:
<template>
<!-- 只有当 showHeavy 变为 true 时,HeavyComponent 才会被加载 -->
<LazyHeavyComponent v-if="showHeavy" />
</template>为什么要懒加载?
- 有些组件体积很大(如地图、图表)
- 有些组件初始时不需要显示(如弹窗、抽屉)
- 懒加载将这些组件的代码单独打包,初始加载时不下载,需要时才加载
- 有效减少首屏 JS 体积,加快首屏速度
什么时候用懒加载?
- ❌ 导航栏、页脚——每个页面都要用,懒加载反而增加请求
- ✅ 地图组件——体积大,只在特定页面用
- ✅ 富文本编辑器——只在编辑页面用
- ✅ 弹窗/对话框——用
v-if控制时
组件命名规则
| 文件路径 | 组件名 | 规则说明 |
|---|---|---|
components/Button.vue | <Button> | 文件名即组件名 |
components/BaseButton.vue | <BaseButton> | 大驼峰命名 |
components/blog/PostCard.vue | <BlogPostCard> | 目录名作为前缀 |
components/blog/post/Card.vue | <BlogPostCard> | 多级目录,中间的 post 被忽略(因为同级有 PostCard) |
命名冲突怎么办?
components/
├── Button.vue → <Button>
└── form/
└── Button.vue → <FormButton> ← 自动加目录名前缀如果同级目录下有同名文件和目录,目录中的组件会有目录名前缀。但如果不确定,最好的做法是给组件起不同的名字,避免冲突。
最佳实践
- 给组件加前缀区分用途:
BaseButton(基础按钮)、AppHeader(应用头部) - 按功能分目录:
form/、card/、table/ - 避免和 UI 库组件重名(如 Element Plus 也有
Button)
组合式函数自动导入
app/composables/ 下以 use 开头的函数自动导入:
// app/composables/useCounter.ts
export const useCounter = (initial = 0) => {
const count = useState('counter', () => initial)
const increment = () => count.value++
const decrement = () => count.value--
return { count, increment, decrement }
}<script setup>
// 直接使用,无需 import
const { count, increment } = useCounter()
</script>组合式函数的设计建议
- 单一职责:一个 composable 做一件事(如
useAuth只管认证,useCounter只管计数) - 返回 ref 而不是普通值:确保响应式链不断裂
- 用 useState 而不是 ref:如果是跨组件共享的状态(SSR 安全)
- 接受参数而不是硬编码:如
useCounter(initial)而不是useCounter() - 做好类型定义:返回值类型清晰,IDE 有提示
工具函数自动导入
app/utils/ 下的函数自动导入:
// app/utils/formatDate.ts
export const formatDate = (date: string) => {
return new Date(date).toLocaleDateString('zh-CN')
}<script setup>
// 直接使用,无需 import
const formatted = formatDate('2025-01-01')
</script>composables/ vs utils/ 怎么选?
| 特征 | composables/ | utils/ |
|---|---|---|
| 命名 | use 前缀 | 无特殊要求 |
| 是否响应式 | 通常包含响应式数据 | 通常不包含 |
| 是否有状态 | 通常有 | 通常无(纯函数) |
| 典型例子 | useAuth()、useCounter() | formatDate()、debounce() |
简单判断
返回值包含 ref/computed?→ composables/;纯输入输出?→ utils/。
Nuxt 内置自动导入
Nuxt 自动导入大量内置 API,无需手动 import:
组合式函数
useFetch, useAsyncData, useState, useCookie, useRoute, useRouter,
useHead, useSeoMeta, useRuntimeConfig, useAppConfig, useNuxtApp,
useLazyFetch, useLazyAsyncData, ...工具函数
navigateTo, abortNavigation, createError, showError, clearError,
defineNuxtPlugin, definePageMeta, defineNuxtRouteMiddleware,
$fetch, callOnce, refreshNuxtData, ...Vue API
ref, reactive, computed, watch, watchEffect, onMounted, onUnmounted,
toRef, toRefs, nextTick, defineProps, defineEmits, ...Vue 的 API 为什么也能自动导入?
Nuxt 配置了 vue 和 @vue/runtime-core 的自动导入。所以你写 ref(0) 时,Nuxt 帮你补了 import { ref } from 'vue'。
好处
代码更简洁,不用每个文件都写 import { ref, computed, onMounted } from 'vue'。
自定义自动导入
从第三方库自动导入
// nuxt.config.ts
export default defineNuxtConfig({
imports: {
presets: [
{
from: 'lodash-es',
imports: ['debounce', 'throttle'],
},
],
},
})什么时候需要?
如果你频繁使用某个库的函数(如 debounce),每次都写 import { debounce } from 'lodash-es' 很烦。配置后直接用 debounce() 就行。
INFO
️ 注意:不要自动导入太多东西 否则 IDE 自动补全会很慢,而且可能出现命名冲突
扫描额外目录
export default defineNuxtConfig({
imports: {
dirs: [
'composables', // 默认
'composables/*/index.ts', // 子目录
'utils/**/*.ts', // 工具函数
],
},
})禁用自动导入
export default defineNuxtConfig({
imports: {
autoImport: false, // 完全禁用
},
})INFO
️ 不建议禁用 Nuxt 的很多特性(组合式函数、Vue API 等)都依赖自动导入。禁用后你需要手动 import 所有东西,失去了 Nuxt 最大的便利性
类型声明
自动导入的类型声明在 .nuxt/imports.d.ts 中自动生成。如果类型不正确,运行:
npx nuxt prepare为什么有时类型提示丢了?
- 新建了 composable 但 IDE 不识别 → 需要重新生成类型
- Git 切换分支后 →
.nuxt/目录可能过时 npm install后 → 运行nuxt prepare更新类型
注意事项
1. 避免命名冲突
// ❌ 危险:和 VueUse 的 useFetch 重名
import { useFetch } from '@vueuse/core'
// ✅ 安全:Nuxt 的 useFetch 是自动导入的,不要从 VueUse 导入同名函数如果必须用 VueUse 的 useFetch?
给它起别名:
import { useFetch as useVueUseFetch } from '@vueuse/core'2. IDE 支持
确保安装了 Nuxt VS Code 插件(Nuxt - Nuxt DevTools),获得:
- 自动补全
- 类型提示
- 跳转定义
- 组件名高亮
3. 显式导入
如果需要,仍然可以手动 import,自动导入只是省略了这步:
// 这两种写法都可以
const { data } = await useFetch('/api/users') // 自动导入
const { data } = await import('#app').then(m => m.useFetch('/api/users')) // 手动导入(不推荐)4. 服务端代码隔离
server/utils/ 的自动导入仅在服务端生效,客户端代码无法使用:
// server/utils/database.ts → 只能在 server/ 下使用
// app/pages/index.vue → 不能使用 server/utils/ 的函数INFO
️ 这是 Nuxt 4 的类型隔离特性 如果你在客户端代码中用了 server/utils/ 的函数,TypeScript 会报错。如果前后端都要用,放在 shared/utils/
知识脉络
Nuxt 生命周期 → 你在这里:自动导入
│
├─→ 相关:组合式函数 API 速查(13章)
│
├─→ 相关:工具函数速查(15章)
│
└─→ 相关:内置组件速查(14章)