Nuxt 自动导入(Auto-imports)完整指南:从内置预设、目录扫描到源码级实现原理
本文围绕 Nuxt 的 Auto-imports 机制展开:它如何让组件、composables、工具函数和 Vue API 无需显式导入即可全局可用,并深入 Nuxt 源码中 nuxt:imports 模块的实际实现,讲清楚预设(presets)、目录扫描、#imports 虚拟模块与禁用控制的底层原理。读完后,你既能正确地在 nuxt.config 中配置各类自动导入选项,也能理解 Nuxt instance is unavailable 这类上下文错误的根源与修复方式。
什么是 Nuxt 自动导入
Nuxt 会自动导入(auto-import)组件、composables 和 Vue.js API,使它们在整个应用中无需显式 import 语句即可直接使用:
<script setup lang="ts">
const count = ref(1) // ref 被自动导入,无需 import { ref } from 'vue'
</script>
得益于约定优于配置的目录结构,Nuxt 会自动扫描并注册以下目录中的文件:
app/components/—— Vue 组件(详见 组件目录说明)app/composables/—— Vue composables(详见 composables 目录说明)app/utils/—— 辅助函数与其他工具(详见 utils 目录说明)
与传统的"全局声明"(如把函数挂到 window/globalThis)不同,Nuxt 的自动导入保留完整的 TypeScript 类型、IDE 补全和提示,并且只把真正在代码中被使用的导入注入到产物中。从源码结构看,这由 unimport 库的按需注入机制实现——编译器在构建期把用到的标识符替换为真实的 import 语句,未使用的绑定不会进入生产代码。
官方文档约定:文档中所有未显式 import 的函数都由 Nuxt 自动导入,可直接使用;完整的自动导入组件、composables 和工具函数参考见 API 章节。
两点补充说明:
- 在
server/目录中,Nuxt 会自动导入server/utils/导出的函数和变量(服务端由 Nitro 处理,详见 server 目录说明)。 - 通过
nuxt.config的imports配置节,还可以为自定义文件夹或第三方包添加自动导入。
内置自动导入清单:预设(Presets)在源码中的位置
Nuxt 内置了大量自动导入的函数和 composables,覆盖数据获取、应用上下文与运行时配置访问、状态管理,以及组件与插件定义:
<script setup lang="ts">
/* useFetch() 是自动导入的 */
const { data, refresh, status } = await useFetch('/api/hello')
</script>
Vue 暴露的响应式 API(ref、computed 等)以及生命周期钩子、辅助函数也由 Nuxt 自动导入:
<script setup lang="ts">
/* ref() 和 computed() 均被自动导入 */
const count = ref(1)
const double = computed(() => count.value * 2)
</script>
这些"内置"导入并非魔法,而是显式声明的预设列表,集中定义在 presets.ts 中,最终汇聚到 defaultPresets 导出:
// packages/nuxt/src/imports/presets.ts
export const defaultPresets: InlinePreset[] = [
...commonPresets, // vue-demi 兼容:isVue2 / isVue3
...granularAppPresets, // 细粒度的 Nuxt 应用内预设
routerPreset, // onBeforeRouteLeave / onBeforeRouteUpdate
vuePreset, // 来自 'vue' 的响应式 API 与生命周期钩子
vueTypesPreset, // 仅类型:Ref、ComputedRef、PropType 等
]
其中 granularAppPresets(presets.ts 第 15-124 行)按功能模块从 #app/composables/* 逐个声明,例如:
| 来源模块 | 自动导入的函数 |
|---|---|
#app/nuxt |
useNuxtApp、tryUseNuxtApp、defineNuxtPlugin、useRuntimeConfig、defineAppConfig |
#app/composables/asyncData |
useAsyncData、useLazyAsyncData、useNuxtData、refreshNuxtData、clearNuxtData、createUseAsyncData |
#app/composables/fetch |
useFetch、useLazyFetch、createUseFetch |
#app/composables/router |
abortNavigation、addRouteMiddleware、defineNuxtRouteMiddleware、navigateTo、useRoute、useRouter |
#app/composables/state |
useState、clearNuxtState |
#app/composables/error |
clearError、createError、isNuxtError、showError、useError |
#app/composables/ssr |
onPrehydrate、prerenderRoutes、useRequestHeader(s)、useResponseHeader、useRequestEvent、useRequestFetch、setResponseStatus |
#app/composables/head |
useHead、useSeoMeta、useServerHead、injectHead 等 |
vuePreset(presets.ts 第 183-263 行)则列出了来自 vue 包的完整名单:withCtx/withDirectives 等 <script setup> 辅助、onMounted/onUnmounted 等生命周期钩子、ref/computed/watch/reactive 等响应式 API、effectScope/onScopeDispose 等 effect 工具,以及 defineComponent、h、inject、nextTick、useModel、useId 等组件级 API。
此外还有两个可选预设(同样在 presets.ts 中):
appCompatPresets:为requestIdleCallback、setInterval等注入兼容 polyfill,由imports.polyfills(默认true)控制,在 module.ts 的 setup 中按需并入;scriptsStubsPreset:一组useScript*的"桩"函数,当检测到代码引用了它们时会自动安装@nuxt/scripts模块(见 transform.ts 第 50-52 行)。
pages 模块还会注入 pagesImportPresets 与 routeRulesPresets,与默认预设合并为 allNuxtPresets(module.ts 第 19-23 行)。
Composables 的上下文规则:理解 "Nuxt instance is unavailable"
使用 Vue 和 Nuxt 内置的 Composition API composables 时,必须意识到其中许多依赖在**正确的上下文(context)**中被调用。
在组件生命周期期间,Vue 通过一个全局变量跟踪当前组件的临时实例(Nuxt 同样跟踪 nuxtApp 的临时实例),并在同一个 tick 结束后将其清除。这在服务端渲染中尤为关键:既能避免跨请求的状态污染(两个用户之间泄漏共享引用),也能避免不同组件之间的泄漏。
这意味着(除极少数例外),不能在 Nuxt 插件、Nuxt 路由中间件或 Vue setup 函数之外使用它们。而且必须同步使用——在调用 composable 之前不能出现 await,唯一的例外是 <script setup> 块、defineNuxtComponent 声明的组件的 setup 函数、defineNuxtPlugin 或 defineNuxtRouteMiddleware 内部,Nuxt 会对这些位置做编译期转换,在 await 之后仍然保持同步上下文。
如果看到类似 Nuxt instance is unavailable 的错误(对应 错误 E1001),基本可以断定是在 Vue 或 Nuxt 生命周期之外的错误位置调用了 Nuxt composable。
错误示例(在模块顶层调用 composable):
// composables/example.ts
// 试图在 composable 外部访问运行时配置
const config = useRuntimeConfig()
export const useMyComposable = () => {
// 在这里访问运行时配置
}
正确示例(在 composable 内部调用,由调用方保证上下文正确):
// composables/example.ts
export const useMyComposable = () => {
// 因为你的 composable 在生命周期的正确位置被调用,
// useRuntimeConfig 在这里可以正常工作
const config = useRuntimeConfig()
// ...
}
从源码看,这条规则的边界在 nuxt.ts 中体现得很直接:tryUseNuxtApp() 会先尝试 Vue 注入上下文(hasInjectionContext() + getCurrentInstance()),再回退到 getNuxtAppCtx(id).tryUse(),两者都失败返回 null;而 useNuxtApp() 在拿不到实例时会直接抛出 NUXT_E1001 诊断——也就是你看到的 "Nuxt instance is unavailable"。
实用建议:
- 在非 SFC 组件中需要 Nuxt 上下文的 composable 时,用
defineNuxtComponent代替defineComponent包装组件; - 想在异步函数中安全使用 Nuxt composables,可以关注
asyncContext实验特性。
目录级自动导入:composables/、utils/ 是如何被扫描的
Nuxt 直接自动导入约定目录中的文件:
app/components/—— Vue 组件;app/composables/—— Vue composables;app/utils/—— 辅助函数和其他工具。
官方示例可参考 examples 目录 中的 features/auto-imports。
注意:自动导入的
ref和computed在组件<template>中不会被自动解包。这是 Vue 对非模板顶层 ref 的工作方式决定的,可参见 Vue 官方文档中 "Caveat when unwrapping in templates" 一节。
扫描行为的核心逻辑在 module.ts 的 setup 中:
- 遍历所有 layer:对每个 layer 收集
srcDir下的composables/、utils/、types/,以及shared/utils/、shared/types/,再加上该 layer 配置的imports.dirs自定义目录; - 支持 layer 级关闭:如果某个 layer 的
config.imports.scan === false,则跳过该 layer 的扫描; - 模块扩展点:通过
imports:dirs钩子允许其他模块(如 pages 模块)追加目录; - 热重启保护:监听
builder:watch,当这些目录本身被创建或删除(addDir/unlinkDir)时触发restart钩子重启 Nuxt——因为目录级的变化无法靠增量更新覆盖。
真正的导出扫描发生在 regenerateImports(module.ts 第 152-186 行)中:调用 unimport 的 scanDirExports(composablesDirs, { fileFilter: file => !isIgnored(file) }) 提取各文件导出的名称,并按 layer 优先级(priorities)为扫描到的导入赋予 priority。随后触发 imports:extend 钩子让模块注册自己的导入,并做命名冲突检测——若第三方/自定义导入与 Nuxt 内置导入同名且优先级不足,会输出 NUXT_B6002 诊断(module.ts 第 170-178 行)提醒开发者。
开发时,composables/ 目录下的任何文件变化都会触发 regenerateImports 重新扫描并更新 imports.mjs、types/imports.d.ts 等模板(module.ts 第 197-210 行),这就是"新增一个 composable 保存后 IDE 立即可用"的原因。
显式导入:#imports 别名
Nuxt 通过 #imports 别名暴露所有自动导入的符号,需要显式导入时可以这样写:
<script setup lang="ts">
import { computed, ref } from '#imports'
const count = ref(1)
const double = computed(() => count.value * 2)
</script>
其实现分两步(见 module.ts 第 122-128 行):
- 生成
build/imports.mjs模板,内容就是当前全部自动导入的 re-export(toExports(await ctx.getImports())); - 把
#imports别名指向buildDir/imports。
而 TransformPlugin(transform.ts) 会在编译期把 #imports 的导入进一步替换为真实的源模块导入(IMPORTS_RE 匹配 '#imports');如果产物中仍出现未经转换的 #imports,模板会打印警告 [nuxt] #imports should be transformed with real imports。值得注意的细节是:对 node_modules 中的依赖,该插件只处理 #imports 替换,不做隐式自动导入注入(transform.ts 第 43-47 行),避免污染依赖代码。
禁用与控制自动导入
imports 配置的完整类型定义在 types/imports.ts 中(ImportsOptions 接口),关键开关如下。
完全禁用自动导入
将 imports.autoImport 设为 false:
// nuxt.config.ts
export default defineNuxtConfig({
imports: {
autoImport: false,
},
})
这会彻底禁用隐式自动导入,但仍可从 #imports 显式导入。从源码看,autoImport 同时影响两处:TransformPlugin 调用 injectImports 时传入的 autoImport 参数(控制是否注入),以及生成 types/imports.d.ts 时——被禁用后该文件只会输出一行注释 // Implicit auto importing is disabled, you can explicitly import from #imports instead.(module.ts 第 277-291 行),从而移除全局类型声明。
模块默认值(module.ts 第 30-45 行)供参考:
defaults: nuxt => ({
autoImport: true,
scan: true,
presets: defaultPresets,
global: false,
imports: [],
dirs: [],
transform: { include: [new RegExp('^' + escapeRE(nuxt.options.buildDir))] },
virtualImports: ['#imports'],
polyfills: true,
})
部分禁用:关闭目录扫描
希望框架级函数(ref、computed 等)保持自动导入,但关闭对自己代码(自定义 composables 等)的自动扫描时,把 imports.scan 设为 false:
// nuxt.config.ts
export default defineNuxtConfig({
imports: {
scan: false,
},
})
此配置下:
ref、computed、watch等框架函数依然无需手动导入;- 自定义 composables 等需要在文件中手动 import。
注意事项:该配置有明确的局限——
- 使用 layers 的项目需要显式导入每个 layer 中的 composables,无法再依赖自动扫描;
- 它会破坏 layer 系统的覆盖(override)机制(扫描到的导入正是靠 layer 优先级实现覆盖的),使用前请确认理解这一副作用。
其他控制手段
结合 ImportsOptions 类型定义,还有几个实用选项:
imports.dirs:追加额外需要扫描的目录(相对srcDir,支持~/别名);imports.dirsScanOptions.filePatterns:扫描文件模式的 glob,默认*.{ts,js,mjs,cjs,mts,cts,tsx,jsx};imports.transform.include/exclude:正则数组,控制哪些文件参与自动导入转换;imports.commentsDisable:魔法注释,默认支持@unimport-disable与@imports-disable,在单个文件头部写上即可对该文件跳过注入;imports.commentsDebug:默认@unimport-debug/@imports-debug,对指定文件打印注入详情,便于调试;imports.global:将工具函数挂载到globalThis(替代构建期转换),默认false。
禁用组件自动导入
组件的自动导入与 composables/工具的自动导入是分别配置的。禁用 ~/components 目录的组件自动导入,把 components.dirs 设为空数组(注意:这不影响模块注册的组件):
// nuxt.config.ts
export default defineNuxtConfig({
components: {
dirs: [],
},
})
从第三方包自动导入
Nuxt 支持为第三方包配置自动导入。提示:如果该包有对应的 Nuxt 模块,模块通常已经替你配好了自动导入,不必重复配置。
例如为 vue-i18n 包启用 useI18n 的自动导入:
// nuxt.config.ts
export default defineNuxtConfig({
imports: {
presets: [
{
from: 'vue-i18n',
imports: ['useI18n'],
},
],
},
})
imports.presets 接受三种形式(见 types/imports.ts):
- 行内预设(
NuxtImportPreset):from+imports数组,如上例,也可用元组写法['useI18n', 'alias', 'vue-i18n']; - 包扫描预设(
NuxtPackageImportPreset):{ package: 'some-lib' },Nuxt 会扫描该包的导出自动注册,支持ignore排除与cache缓存; - unimport 内置预设名(
NuxtImportPresetName):直接传预设名字符串。
每个预设还支持 priority(同名导入时高优先级胜出,默认 1)、disabled、dtsDisabled(不参与类型声明)、type/typeFrom(仅类型导入)等选项。用户配置的预设会与 Nuxt 默认预设合并——模块 setup 中先 klona 深拷贝 defaultPresets(防止被 hook 订阅者原地修改),再经过 imports:sources 钩子允许模块扩展,最后交给 createUnimport 建立统一的注入上下文(module.ts 第 46-120 行)。
工作原理小结:一次构建中发生了什么
把上述源码证据串起来,Nuxt 自动导入的完整链路是:
- 注册:
nuxt:imports模块在modules:done时创建 unimport 上下文(createUnimport,parser 使用oxc),合并默认/模块/用户预设; - 扫描:对
composables/、utils/等目录执行scanDirExports,叠加imports:extend钩子注册的导入,并做冲突诊断; - 产物模板:生成
build/imports.mjs(#imports别名目标)与types/imports.d.ts(全局类型声明)、types/shared-imports.d.ts(client/server 共享上下文的类型); - 编译期注入:
nuxt:imports-transform插件(enforce: 'post')对 Vue SFC 与 JS 文件调用ctx.injectImports,把用到的标识符替换为真实 import; - 类型系统:
generateTypeDeclarations输出全局声明,配合resolveTypePaths把from解析为构建产物相对路径,保证 IDE 补全与跳转准确。
这套"按需注入 + 全局类型"的设计,正是 Nuxt 能在保留类型安全与 IDE 体验的同时,做到"生产代码只包含实际使用内容"的根本原因。
参考路径速查
| 内容 | 仓库路径 |
|---|---|
| 官方文档(本文骨架来源) | docs/3.guide/1.concepts/3.auto-imports.md |
| 模块实现(扫描、模板、类型) | packages/nuxt/src/imports/module.ts |
| 内置预设清单 | packages/nuxt/src/imports/presets.ts |
| 编译期转换插件 | packages/nuxt/src/imports/transform.ts |
imports 配置类型定义 |
packages/schema/src/types/imports.ts |
useNuxtApp / tryUseNuxtApp 实现 |
packages/nuxt/src/app/nuxt.ts |
| E1001 错误说明 | docs/errors/e1001.md |
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00