使用层
本地层
layers/ 目录
放在项目的 layers/ 目录下,自动注册:
my-app/
├── layers/
│ ├── admin/
│ │ ├── nuxt.config.ts
│ │ └── app/
│ │ ├── pages/
│ │ └── components/
│ └── blog/
│ ├── nuxt.config.ts
│ └── app/
│ └── pages/
└── nuxt.config.ts自动注册的条件
layers/ 下的每个子目录必须包含 nuxt.config.ts,否则不会被识别为层。
控制优先级
使用数字前缀控制加载顺序:
layers/
├── 1.base/ # 最低优先级
├── 2.theme/ # 中等优先级
└── 3.admin/ # 最高优先级数字前缀的原理:Nuxt 按文件名字母顺序加载层,数字前缀确保了可预测的加载顺序。后加载的层覆盖先加载的层。
什么时候需要控制优先级?
当多个层定义了相同的文件(如 app/pages/index.vue),优先级决定哪个版本生效。没有冲突时不需要数字前缀。
extends 配置
本地路径
export default defineNuxtConfig({
extends: [
'../shared', # 相对路径
'./layers/admin', # 项目内路径
],
})相对路径基于什么?
基于 nuxt.config.ts 所在目录。'../shared' 指向项目同级目录下的 shared 文件夹。
npm 包
export default defineNuxtConfig({
extends: [
'@myorg/nuxt-theme', # npm 包
'@myorg/nuxt-admin-layer', # 带 scope 的包
],
})发布层到 npm:
{
"name": "@myorg/nuxt-theme",
"main": "./nuxt.config.ts",
"dependencies": { ... }
}npm 层的适用场景:多个项目共享同一套 UI 主题或功能模块。发布后,任何 Nuxt 项目 extends 即可使用,无需复制代码。
Git 仓库
export default defineNuxtConfig({
extends: [
'github:user/repo', # GitHub 默认分支
'github:user/repo#v1.0.0', # 指定 tag
'github:user/repo#dev', # 指定分支
'github:user/repo/subdir', # 子目录
'gitlab:user/repo', # GitLab
'bitbucket:user/repo', # Bitbucket
],
})Git 远程层的风险
每次 nuxt prepare 都可能从远程拉取,网络不稳定时构建会失败。生产环境建议使用 npm 包或本地路径,更可靠。
安装远程依赖
export default defineNuxtConfig({
extends: [
['github:user/repo', { install: true }], // 自动安装 npm 依赖
],
})什么时候需要 install: true?
远程层如果有 package.json 中声明的 npm 依赖,需要 install: true 才能自动安装。否则需要手动在项目中安装这些依赖。
层覆盖与合并
文件覆盖
项目文件 > 层文件:
# 层中的文件
layer/app/pages/about.vue → 层版本
# 项目中的文件(优先)
app/app/pages/about.vue → 项目版本(使用这个)覆盖规则:项目自身的文件永远优先。这意味着你可以在不修改层代码的情况下,用项目文件覆盖层中的任何页面、组件或布局。
组件也会被覆盖吗?
是的。如果层定义了 app/components/MyButton.vue,项目中也定义了同名组件,项目版本优先。这在定制第三方层的 UI 时很有用。
配置合并
数组配置会合并,对象配置会深度合并:
// 层配置
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/app/assets/layer.css'],
})
// 项目配置
export default defineNuxtConfig({
extends: ['./layer'],
modules: ['@pinia/nuxt'], // 合并:['@nuxt/ui', '@pinia/nuxt']
css: ['~/app/assets/app.css'], // 合并:['layer.css', 'app.css']
})合并规则详解
| 配置类型 | 合并行为 | 示例 |
|---|---|---|
数组(modules、css、plugins) | 追加合并 | ['@nuxt/ui'] + ['@pinia/nuxt'] → ['@nuxt/ui', '@pinia/nuxt'] |
对象(runtimeConfig、app) | 深度合并 | 层的 runtimeConfig.public 与项目的合并 |
| 基本类型(字符串、数字) | 项目值覆盖层值 | 项目的 ssr: false 覆盖层的 ssr: true |
常见陷阱
routeRules 是对象类型,会深度合并。如果层和项目都定义了 /api/** 的规则,会合并而不是覆盖。如果需要完全替换,使用 routeRules 的覆盖模式。
禁用层中的模块
export default defineNuxtConfig({
extends: ['./layer-with-image'],
image: false, // 禁用层中的 @nuxt/image
})使用场景:层的 nuxt.config.ts 中注册了 @nuxt/image,但你的项目不需要图片优化功能(或用自己的方案),可以禁用它以减少构建时间和包体积。
层可以包含什么?
| 目录/文件 | 层中可用 | 说明 |
|---|---|---|
app/pages/ | ✅ | 添加页面路由 |
app/components/ | ✅ | 添加可复用组件 |
app/composables/ | ✅ | 添加组合式函数 |
app/layouts/ | ✅ | 添加布局模板 |
app/middleware/ | ✅ | 添加路由中间件 |
app/plugins/ | ✅ | 添加插件 |
server/api/ | ✅ | 添加 API 路由 |
server/middleware/ | ✅ | 添加服务端中间件 |
server/utils/ | ✅ | 添加工具函数 |
shared/ | ✅ | 添加共享类型和工具 |
public/ | ✅ | 添加静态资源 |
nuxt.config.ts | ✅ | 层自己的配置 |
app.vue | ⚠️ 谨慎 | 会覆盖项目的 app.vue |
为什么 app.vue 要谨慎?
app.vue 是单文件,项目级只能有一个。如果层定义了 app.vue,会影响所有使用该层的项目。通常不应在层中定义 app.vue。
实战场景
场景一:项目拆分——管理后台 + 用户端
my-app/
├── layers/
│ └── admin/ # 管理后台作为层
│ ├── nuxt.config.ts
│ └── app/
│ ├── pages/
│ │ └── admin/ # /admin/** 路由
│ ├── components/
│ │ └── admin/ # 管理后台专用组件
│ └── layouts/
│ └── admin.vue
├── app/
│ ├── pages/ # 用户端路由
│ └── components/ # 用户端组件
└── nuxt.config.ts好处:管理后台和用户端在同一个项目中,共享数据库连接、工具函数、类型定义,但页面和组件互不干扰。
场景二:SaaS 多租户主题
// nuxt.config.ts
export default defineNuxtConfig({
extends: [
`./layers/themes/${process.env.NUXT_THEME || 'default'}`,
],
})layers/
└── themes/
├── default/ # 默认主题
├── premium/ # 高级主题
└── enterprise/ # 企业主题好处:同一套业务逻辑,通过环境变量切换主题层。每个主题层可以覆盖组件样式、布局、页面。
场景三:共享功能层
// nuxt.config.ts
export default defineNuxtConfig({
extends: [
'./layers/auth', # 认证层:登录页、中间件、composables
'./layers/analytics', # 分析层:埋点插件、页面追踪
],
})好处:跨项目复用功能模块。新建项目时 extends 即可获得完整的认证和分析功能。
调试层
查看层的加载情况:
nuxt info查看所有已注册的层、模块和自动导入。
更详细的调试:
# 查看最终合并后的配置
nuxt config show
# 查看层的加载顺序和路径
DEBUG=nuxt:layers nuxt dev常见问题
路径别名问题
层中的 ~ 和 @ 指向使用层的项目,不是层本身。解决方案:
// 层的 nuxt.config.ts
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path'
const currentDir = dirname(fileURLToPath(import.meta.url))
export default defineNuxtConfig({
css: [
join(currentDir, './app/assets/layer.css'), // 使用绝对路径
],
})为什么会这样?
Nuxt 的 ~ 和 @ 别名在运行时解析为项目根目录,不是层目录。这是设计决定——层的代码在项目上下文中执行,路径别名指向项目确保了一致性。
远程层依赖
远程层的 npm 依赖在特殊位置(node_modules/.c12/),ESLint 等工具可能无法访问。解决方案:
- 将依赖声明为项目的直接依赖
- 使用
install: true自动安装
层中的 auto-imports 冲突
如果多个层定义了同名的 composable 或组件:
layers/admin/app/composables/useAuth.ts → useAuth()
layers/blog/app/composables/useAuth.ts → useAuth() ← 冲突!解决方案:给 composable 加命名空间前缀:
// layers/admin/app/composables/useAdminAuth.ts
// layers/blog/app/composables/useBlogAuth.ts层中的 server middleware 执行顺序
所有层中的 server/middleware/ 都会生效,按字母顺序执行。如果多个层定义了同名中间件文件(如 auth.ts),只有优先级最高的层的版本会被使用(文件覆盖规则),不是都执行。
层的热更新
本地层(layers/ 目录下)的文件修改会触发热更新。远程层(npm 包、Git 仓库)需要重启开发服务器才能生效。