深度解析 Nuxt 目录结构:组织全栈 Vue 应用的完整指南 深度解析 Nuxt 目录结构组织全栈 Vue 应用的完整指南【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt本文基于 Nuxt 官方文档docs/2.directory-structure/index.md及其配套子文档系统讲解一个 Nuxt 应用中各个标准目录app/、server/、shared/、public/、modules/、layers/等的职责、约定与自动注册机制并结合当前仓库中的 monorepo 源码组织与测试夹具说明这些目录约定在框架内部是如何落地和被验证的。读完本文你将能够从零规划一个结构清晰、符合 Nuxt 惯例的全栈应用目录并理解每个目录背后的自动导入、路由生成与构建行为。1. 目录结构总览Nuxt 应用有一套被刻意设计为“易于理解且一致使用”的目录结构目录即约定文件放在正确的位置就会被自动扫描、自动导入或自动注册为路由。官方文档 目录结构总览 描述的完整布局如下综合各子文档示例整理-| project/ ---| nuxt.config.ts # 主配置文件确定项目根目录的标志 ---| .nuxtrc # 可选扁平语法的配置 ---| .nuxtignore # 可选构建阶段忽略文件 ---| .env # 可选环境变量 ---| app/ # 应用主目录客户端 SSR 代码 -----| app.vue # 应用根组件 -----| app.config.ts # 响应式应用配置 -----| error.vue # 错误页 -----| assets/ # 由构建工具Vite/webpack处理的资源 -----| components/ # 自动导入的 Vue 组件 -----| composables/ # Vue composables -----| layouts/ # 页面布局组件 -----| middleware/ # 路由级前端中间件 -----| pages/ # 基于文件的路由 -----| plugins/ # Nuxt 应用插件 -----| utils/ # 应用内工具函数 ---| public/ # 静态资源原样按根路径提供 ---| server/ # 服务端Nitro代码 -----| api/ # /api 前缀的 API 路由 -----| routes/ # 不带 /api 前缀的服务路由 -----| middleware/ # 服务端中间件 -----| plugins/ # 服务端插件 -----| utils/ # 服务端工具函数 -----| types/ # 仅在服务端自动导入的类型 ---| shared/ # 应用与服务端共享的代码 -----| utils/ # 两端自动导入的工具函数 -----| types/ # 两端自动导入的类型 ---| modules/ # 本地模块自动注册 ---| layers/ # 本地层自动注册 ---| content/ # 文件式 CMS由 Nuxt Content 模块启用 ---| test/ # 应用测试unit/nuxt/e2e ---| .nuxt/ # 开发期生成目录应加入 .gitignore ---| .output/ # 生产构建输出目录应加入 .gitignore两个关键的“判定性”事实根目录Nuxt 应用的根目录就是包含nuxt.config.ts的目录该文件是应用的配置入口app/主目录Nuxt 4 将应用代码统一收敛在app/目录中而非 Nuxt 3 时代的根目录pages/、components/其中pages/等子目录的存在与否会直接影响依赖是否被引入——例如没有pages/时不会引入 vue-router。当前仓库本身就遵循并验证了这一结构仓库根的 nuxt.config.ts 定义了 Nuxt 项目自身test/fixtures/basic/是一个包含app/、server/、modules/、layers/与custom-public/的完整测试应用用于在 CI 中验证上述目录约定的实际行为。2. 根目录与配置文件2.1nuxt.config.ts应用的主配置文件扩展名可以是.js、.ts或.mjs。defineNuxtConfig辅助函数全局可用无需导入也可以显式从nuxt/config导入见 nuxt.config.ts 文档export default defineNuxtConfig({ // 我的 Nuxt 配置 })Nuxt 在检测到主配置文件、.env、.nuxtignore或.nuxtrc变化时会执行完整重启因此这些文件变更不会通过 HMR 热更新。2.2.nuxtrc.nuxtrc提供基于unjs/rc9的扁平语法配置适合全局性配置。示例摘自 .nuxtrc 文档# 禁用 SSR ssrfalse # 配置 nuxt/devtools devtools.enabledtrue # 添加 Nuxt 模块 modules[]nuxt/image modules[]nuxt-security # 模块安装状态由 Nuxt 自动写入勿手动修改 setups.nuxt/test-utils3.23.0优先级为nuxt.config 项目级.nuxtrc 全局~/.nuxtrcmacOS/Linux或C:\Users\{username}\.nuxtrcWindows。Nuxt 会自动维护其中的setups段落来跟踪模块的安装与升级状态该段落不应手工编辑。2.3.nuxtignore.nuxtignore让 Nuxt 在构建阶段忽略根目录中的指定文件其规则与.gitignore完全相同每行一个 glob 模式。官方示例# 忽略布局 app/layouts/foo.vue app/layouts/foo.vue # 忽略名字以 -ignore.vue 结尾的布局 app/layouts/*-ignore.vue # 忽略页面 app/pages/bar.vue app/pages/bar.vue # 忽略 ignore 文件夹内的页面 app/pages/ignore/*.vue # 忽略 foo 文件夹下的中间件但保留 foo/bar.js app/middleware/foo/*.js !app/middleware/foo/bar.js此外还可以在nuxt.config中用ignoreOptions、ignorePrefix、ignore配置项实现同样的忽略行为见 .nuxtignore 文档。3.app/应用目录app/是 Nuxt 应用的主目录包含应用前端的全部约定目录与三个特殊文件。3.1app.vue根组件app.vue是应用的根组件app.vue 文档。它的三种典型用法!-- 最小用法没有 pages/ 时app.vue 就是整个应用 -- template h1Hello World!/h1 /template!-- 配合 pages/必须用 NuxtPage / 渲染当前页面 -- template NuxtPage / /template!-- 配合 layouts/用 NuxtLayout / 包裹 NuxtPage / -- template NuxtLayout NuxtPage / /NuxtLayout /template注意app.vue中引入的任何 JS 与 CSS 都是全局的会被包含进每一个页面存在pages/时app.vue是可选的Nuxt 会自动提供默认实现但你仍可自行定制。3.2app.config.ts响应式应用配置app.config.ts扩展名可为.ts/.js/.mjs暴露一份响应式的应用内配置可在运行时通过插件或生命周期更新并支持 HMRapp.config 文档export default defineAppConfig({ theme: { primaryColor: #ababab, }, })在 SSR 与浏览器中均可通过useAppConfig()访问并用updateAppConfig()在运行时更新script setup const appConfig useAppConfig() // { foo: bar } const newAppConfig { foo: baz } updateAppConfig(newAppConfig) console.log(appConfig) // { foo: baz } /script约束与限制绝不能存放密钥——该配置会暴露到客户端 bundle由于app.config.ts与 Nitro 共享处理流程不能在其中直接导入 Vue 组件部分自动导入在 Nitro 上下文也不可用完整推断的类型只在应用代码中可用在server/、shared/与nuxt.config中app.config的键类型化为unknown。需要跨上下文类型时可扩展SharedAppConfig/AppConfigInput/AppConfig接口均通过declare module nuxt/schema声明合并层layers之间合并app.config时采用基于 defu 函数合并器的自定义策略数组值可通过返回数组的函数自定义合并行为但该函数合并器只能用于扩展层、不能用于主项目的app.config。3.3error.vue错误页运行时出现意外错误时error.vue用于覆盖默认错误页并友好地展示错误error.vue 文档script setup langts import type { NuxtError } from #app const props defineProps{ error: NuxtError }() /script template div h1{{ error.status }}/h1 NuxtLink to/返回首页/NuxtLink /div /template该文件接收唯一的errorprop其结构为interface NuxtError { status: number fatal: boolean unhandled: boolean statusText?: string data?: unknown cause?: unknown }要点它不是路由不应放在pages/中也不要用definePageMeta但可以通过NuxtLayout组件使用布局。自定义错误字段应放在data里如throw createError({ status: 404, statusText: Page Not Found, data: { myCustomField: true } })否则会被丢弃。3.4pages/基于文件的路由pages/目录中每个 Vue 组件都会被自动注册为一条路由pages 文档app/pages/index.vue映射到/使用.vue/.js/.jsx/.mjs/.ts/.tsx等 Nuxt 支持的有效扩展名均可该目录可选不存在时不会引入 vue-router适合落地页要强制启用可设置pages: true页面必须有单一根元素HTML 注释也算元素否则客户端路由切换时转场会失败。动态路由方括号即参数双方括号为可选参数[...]为全捕获路由-| pages/ ---| index.vue ---| users-[group]/ -----| [id].vue # 匹配 /users-admins/123template p{{ $route.params.group }} - {{ $route.params.id }}/p /template[[slug]].vue同时匹配/与/test[...slug].vue匹配该路径下的所有路由如/hello/world时$route.params.slug为[hello, world]。访问参数可经$routeOptions API或useRoute()组合式 API。路由分组用括号目录(marketing)/分组不影响 URL 结构从 v4.3 起分组信息自动写入route.meta.groups可在组件中做条件判断。definePageMeta页面元数据编译器宏可定义alias、keepalive、key、layout、layoutTransition/pageTransition、middleware、name、path、props等特殊元数据嵌套路由的 meta 会合并为单一对象。它不能引用响应式数据或有副作用的函数但可引用导入绑定与局部纯函数嵌套目录中parent/child.vueparent.vue会生成父子路由需在内层模板插入NuxtPage。导航声明式用内置的NuxtLink无需导入编程式用navigateTo()务必await或返回其结果script setup langts const name ref() function navigate () { return navigateTo({ path: /search, query: { name: name.value } }) } /script客户端/服务端页面.client.vue后缀的页面不在服务端渲染任何内容.server.vue后缀的页面由服务端组件自动渲染渲染代码不进入客户端 bundle必须有单一根元素。多pages/目录通过 Nuxt 层实现页面分组例如在nuxt.config中extends: [./some-app]让some-app/pages/的页面参与主应用路由。3.5components/组件目录components/中的组件以及模块注册的组件会被自动导入components 文档。组件名由路径决定重复段被去除components/base/foo/Button.vue→BaseFooButton /分组目录加括号(foo)/则不参与命名得到BaseButton /在nuxt.config中把pathPrefix: false可只按文件名注册Nuxt 2 风格。核心用法速览动态组件component :is...需要用 Vue 的resolveComponent参数必须是字面量字符串或直接从#components导入组件传入is懒加载组件名加Lazy前缀按需加载对应 chunk延迟水合hydrate-on-visible、hydrate-on-idle、hydrate-on-interaction、hydrate-on-media-query、hydrate-after、hydrate-when、hydrate-never等属性控制组件何时变为可交互组件水合完成会触发hydrated事件全局组件放在components/global/或使用.global.vue后缀每个全局组件是独立 chunk不要滥用自定义目录与过滤通过components: [...]数组可注册任意目录支持pathPrefix、prefix、pattern、ignore、extensions等选项嵌套目录需先声明按顺序扫描客户端组件.client后缀使其仅在客户端渲染只对自动导入与#components导入生效服务端组件.server后缀声明仅服务端渲染的“Islands”组件底层基于NuxtIsland也可与同名.client组件配对形成服务端/客户端双实现。3.6 其余约定目录composables/放置 Vue composables自动导入composables 文档layouts/包裹页面、避免页面切换时重渲染外围结构的布局组件配合NuxtLayout使用layouts 文档middleware/导航到特定路由之前执行的前端路由中间件middleware 文档plugins/在 Nuxt 应用创建时使用的 Vue 插件plugins 文档utils/应用中可在组件、composables、页面内使用的工具函数utils 文档assets/交由构建工具Vite 或 webpack处理的网站资源assets 文档。4.public/静态资源目录public/中的文件按根路径原样提供不经过构建流程处理public 文档。它适合必须保持文件名的文件如robots.txt或基本不会变化的文件如favicon.ico-| public/ ---| favicon.ico ---| og-image.png ---| robots.txtscript setup langts useSeoMeta({ ogImage: /og-image.png, }) /script需要区分的是public/是“原样分发”而app/assets/是“构建处理”——后者会经过打包、压缩与指纹化。5.server/服务端目录server/包含应用的服务端代码Nuxt 会自动扫描其中的文件并注册 API 与服务端处理器开发时支持 HMRserver 文档。每个文件应导出一个用defineEventHandler()别名eventHandler()定义的处理函数可直接返回 JSON、Promise或Response对象// server/api/hello.ts → 路由 /api/hello import { defineEventHandler } from nitro/h3 export default defineEventHandler((event) { return { hello: world } })页面中即可通用调用const { data } await useFetch(/api/hello)。子目录职责目录职责server/api/API 路由自动加/api前缀server/routes/不加/api前缀的服务路由如动态/sitemap.xmlserver/middleware/每个请求在任何服务路由之前执行用于加/检头、记录请求、扩展上下文不应返回或结束请求server/plugins/注册为 Nitro 插件扩展 Nitro 运行时行为与生命周期钩子server/utils/服务端自定义工具函数可被服务端代码自动导入server/types/仅在服务端上下文自动导入的类型只扫描直接子文件常用配方均来自 server 文档动态参数server/api/hello/[name].ts中用getRouterParam(event, name)读取HTTP 方法匹配文件名加.get、.post、.put、.delete等后缀方法不匹配返回 405可用index.[method].ts构造 API 命名空间全捕获路由server/api/foo/[...].ts兜底所有未匹配请求[...slug].ts可命名并读取参数请求体readBody(event)GET 上调用会抛 405配合.post.ts文件查询参数getQuery(event)错误处理未捕获错误返回 500其他错误码用createError({ status, statusText })抛出自定义状态码用setResponseStatus(event, 202)运行时配置服务端用useRuntimeConfig()密钥经.env的NUXT_前缀变量注入#server别名v4.3在server/内任意深度用import ... from #server/utils/formatUser导入但不能在客户端代码中使用后台任务event.waitUntil(promise)在响应发出后继续等待异步任务完成。边界规则不要在服务端路由/工具中导入 Vue 应用代码也不要在应用中导入服务端专用代码——两者运行在不同 bundle 与上下文中原因详见shared/一节。高级用法还包括nitro配置项、createRouter嵌套路由、sendStream流式响应、sendRedirect重定向、fromNodeMiddleware遗留适配以及通过nitro.storage或 Nitro 插件挂载 Redis 等 unstorage 驱动的存储层。6.shared/共享目录shared/用于存放可同时被 Vue 应用和 Nitro 服务端使用的代码自 Nuxt v3.14 起可用shared 文档。为什么不能混用 Vue 与 Nitro 代码Nuxt 构建两个彼此独立的 bundle——Vue 应用客户端 SSR与 Nitro 服务端API 路由、中间件、插件它们在不同上下文运行。Vue 侧代码依赖useNuxtApp()、useRoute()等应用上下文Nitro 侧代码可能依赖 Node API。shared/的代码进入两个 bundle因此不能导入 Vue 代码也不能导入 Nitro 代码。即使import type能在编译期擦除官方仍建议把跨端类型放在shared/types/以匹配应用/服务端/共享三个类型上下文的项目引用划分。使用方式shared/utils/与shared/types/中的文件会被两端自动导入扫描规则与app/composables/、app/utils/相同嵌套子目录不自动导入其他位置的文件用#shared别名显式导入// shared/utils/capitalize.ts export const capitalize (input: string) { return input[0] ? input[0].toUpperCase() input.slice(1) : }!-- app/app.vue自动导入无需 import -- script setup langts const hello capitalize(hello) /script// server/api/hello.get.ts同样自动导入 export default defineEventHandler((event) { return { hello: capitalize(hello) } })// 需要显式导入时用 #shared 别名 import capitalize from #shared/capitalize import lower from #shared/formatters/lower import upper from #shared/utils/formatters/upper7.modules/本地模块目录modules/是存放本地模块的推荐位置匹配以下模式的文件会被自动注册无需在nuxt.config中再次声明modules 文档modules/*/index.tsmodules/*.ts官方示例模块定义 运行时 API 路由// modules/hello/index.ts // nuxt/kit 是本地模块可用的子路径导入无需将 nuxt/kit 加入项目依赖 import { addComponentsDir, addServerHandler, createResolver, defineNuxtModule } from nuxt/kit export default defineNuxtModule({ meta: { name: hello }, setup () { const resolver createResolver(import.meta.url) // 添加 API 路由 addServerHandler({ route: /api/hello, handler: resolver.resolve(./runtime/api-route), }) // 添加组件目录 addComponentsDir({ path: resolver.resolve(./runtime/app/components), pathPrefix: true, // 加前缀避免与用户代码或其他模块冲突 }) }, })// modules/hello/runtime/api-route.ts import { defineEventHandler } from nitro/h3 export default defineEventHandler(() { return { hello: world } })执行顺序先加载nuxt.config中声明的模块再按字母序执行modules/中的本地模块目录名加数字前缀1.first-module/、2.second-module.ts可调整顺序。注意本地模块中会放在app/的文件组件、页面、composables 等必须放在modules/your-module/runtime/app/下以保证类型检查正确。从源码结构看本地模块机制由packages/kit提供——仓库中 packages/kit/src/module/ 目录承载模块注册、生命周期钩子等实现packages/nuxt的核心启动流程packages/nuxt/src/core/会按上述顺序装配模块。8.layers/层目录layers/用于组织与共享可复用的代码、组件、composables 与配置其中的层自动注册layers 文档该能力自 Nuxt v3.12.0 起可用。典型结构-| layers/ ---| base/ -----| nuxt.config.ts # 每个层必须有 nuxt.config.ts可为空 -----| app/ -------| components/ ---------| BaseButton.vue -------| composables/ ---------| useBase.ts -----| server/ -------| api/ ---------| hello.ts ---| admin/ -----| nuxt.config.ts -----| app/ -------| pages/ ---------| admin.vue -------| layouts/ ---------| admin.vue要点每个层都是一个迷你 Nuxt 应用可包含nuxt.config.ts、app.config.ts、app/components|composables|utils|pages|layouts|middleware|plugins/、server/、shared/自动别名v3.16.0每层的srcDir都有#layers/[name]别名如import { useAdmin } from #layers/admin/composables/useAdmin优先级多个层定义同一资源时优先级高者胜出层按字母排序靠后的字母优先级更高Z A可用数字前缀1.base/、2.features/、3.admin/或extends数组第一个条目优先级最高控制顺序。当前仓库的测试夹具test/fixtures/layers-fixture/与packages/nuxt/test/layers-fixture/正是用于验证层合并、别名与优先级行为的测试工程。9.content/文件式 CMScontent/目录由Nuxt Content模块启用模块解析其中的.md、.yml、.csv、.json文件为应用提供文件式 CMS 能力——内置组件渲染内容、MongoDB 风格查询 API、在 Markdown 中以 MDC 语法使用 Vue 组件、自动生成导航content 文档。启用只需一条命令安装模块并写入nuxt.config.tsnpx nuxt module add content随后把 Markdown 放入content/如content/index.md并用全捕获路由 ContentRenderer渲染!-- app/pages/[...slug].vue -- script langts setup const route useRoute() const { data: page } await useAsyncData(route.path, () { return queryCollection(content).path(route.path).first() }) /script template div ContentRenderer v-ifpage :valuepage / /div /template10.test/测试目录test/是应用测试单元测试、Nuxt 运行时测试、端到端测试的推荐位置。Nuxt不会像扫描app/或server/那样扫描它运行器与布局由你自己决定通常配合nuxt/test-utils。常见的环境分离布局test 文档-| test/ ---| e2e/ # 针对运行中应用的端到端测试 ---| nuxt/ # 需要 Nuxt 运行时环境的测试 ---| unit/ # 不依赖 Nuxt 运行时的快速 Node 测试从仓库实践看test/e2e/、test/nuxt/ 与 test/fixtures/ 的组织方式正是这一约定的体现fixture 目录承载各种最小化应用basic、minimal、spa、vapor等供测试在隔离环境中启动 Nuxt 并断言行为。11. 生成目录.nuxt/与.output/.nuxt/文档开发期由 Nuxt 根据目录结构生成的虚拟文件目录是理解 Nuxt 生成物入口、插件、类型等的最佳学习材料。Nuxt 还为模块提供了虚拟文件系统VFS允许模块向该目录添加模板而不落盘可在开发模式下通过 Nuxt DevTools 的 Virtual Files 页签浏览。整个目录在每次nuxt dev时重建不要手动修改其中文件并应加入.gitignore.output/文档生产构建nuxt build的输出目录即部署产物同样会在构建时整体重建应加入.gitignore。12. 目录结构在仓库源码中的落地将官方约定与本仓库的实现对照可以进一步确认各目录机制的来源应用与页面app/目录的装配、pages/的路由生成逻辑位于 packages/nuxt/src/pages/相关行为由 packages/nuxt/test/pages.test.ts、packages/nuxt/test/normalize-routes.test.ts 等测试覆盖组件自动导入扫描、命名pathPrefix、prefix、括号分组与Lazy/.client/.server后缀处理在 packages/kit/src/components.ts 中实现配套大量测试如 packages/nuxt/test/component-names.test.ts、packages/nuxt/test/scan-components.test.ts服务端目录server/的 Nitro 集成api/、routes/、中间件、插件扫描在 packages/nuxt/src/core/ 与 packages/nitro-server/ 中完成层与忽略规则层合并逻辑在 packages/kit/src/layers.ts.nuxtignore解析在 packages/kit/src/ignore.ts含 packages/kit/src/ignore.test.ts 验证可运行示例仓库自带的 playground/ 是一个可启动的最小应用——playground/app/app.vue 为应用根组件playground/server/api/test.ts 展示server/api/路由写法playground/nuxt.config.ts 为配置文件。13. 总结目录职责速查表路径职责关键约定nuxt.config.ts应用主配置存在该文件即项目根变更触发完整重启.nuxtrc/.nuxtignore/.env扁平配置 / 构建忽略 / 环境变量变更均触发完整重启app/app.vue应用根组件有pages/时需用NuxtPage /app/pages/文件式路由可选单根元素[param]、[[param]]、[...slug]、(group)app/components/自动导入组件路径决定命名Lazy、.client、.server、global/app/composables/、app/utils/自动导入的 composables 与工具函数扫描规则同shared/utilsapp/layouts/、app/middleware/、app/plugins/布局 / 前端路由中间件 / 应用插件自动注册app/app.config.ts响应式应用配置经useAppConfig()访问勿放密钥app/error.vue全局错误页接收error: NuxtErrorproppublic/原样按根路径分发的静态文件不经过构建server/api|routes|middleware|plugins|utils|typesNitro 服务端代码自动扫描注册#server别名v4.3shared/应用与服务端共享代码v3.14shared/utils与shared/types双端自动导入#shared别名modules/本地模块modules/*.ts、modules/*/index.ts自动注册字母序执行layers/可复用层v3.12 自动注册每层必须有nuxt.config.ts#layers/[name]别名v3.16content/文件式 CMS需 Nuxt Content 模块.md/.yml/.csv/.jsontest/应用测试不被 Nuxt 扫描自行组织 unit/nuxt/e2e.nuxt//.output/开发生成目录 / 生产构建产物均加入.gitignore勿手动修改掌握这套“目录即约定”的体系后你就可以用最小的心智负担组织一个 Nuxt 全栈应用把文件放进正确的目录自动导入、路由、API 端点与模块注册都会由框架代为完成而你只需在需要突破默认行为时自定义components目录、ignore规则、层优先级、模块顺序再回到nuxt.config中显式声明。【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考