Skip to content

使用层

本地层

layers/ 目录

放在项目的 layers/ 目录下,自动注册:

text
my-app/
├── layers/
│   ├── admin/
│   │   ├── nuxt.config.ts
│   │   └── app/
│   │       ├── pages/
│   │       └── components/
│   └── blog/
│       ├── nuxt.config.ts
│       └── app/
│           └── pages/
└── nuxt.config.ts

自动注册的条件

layers/ 下的每个子目录必须包含 nuxt.config.ts,否则不会被识别为层。

控制优先级

使用数字前缀控制加载顺序:

text
layers/
├── 1.base/          # 最低优先级
├── 2.theme/         # 中等优先级
└── 3.admin/         # 最高优先级

数字前缀的原理:Nuxt 按文件名字母顺序加载层,数字前缀确保了可预测的加载顺序。后加载的层覆盖先加载的层。

什么时候需要控制优先级?

当多个层定义了相同的文件(如 app/pages/index.vue),优先级决定哪个版本生效。没有冲突时不需要数字前缀。

extends 配置

本地路径

ts
export default defineNuxtConfig({
  extends: [
    '../shared',           # 相对路径
    './layers/admin',      # 项目内路径
  ],
})

相对路径基于什么?

基于 nuxt.config.ts 所在目录。'../shared' 指向项目同级目录下的 shared 文件夹。

npm 包

ts
export default defineNuxtConfig({
  extends: [
    '@myorg/nuxt-theme',       # npm 包
    '@myorg/nuxt-admin-layer',  # 带 scope 的包
  ],
})

发布层到 npm:

json
{
  "name": "@myorg/nuxt-theme",
  "main": "./nuxt.config.ts",
  "dependencies": { ... }
}

npm 层的适用场景:多个项目共享同一套 UI 主题或功能模块。发布后,任何 Nuxt 项目 extends 即可使用,无需复制代码。

Git 仓库

ts
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 包或本地路径,更可靠。

安装远程依赖

ts
export default defineNuxtConfig({
  extends: [
    ['github:user/repo', { install: true }],  // 自动安装 npm 依赖
  ],
})

什么时候需要 install: true

远程层如果有 package.json 中声明的 npm 依赖,需要 install: true 才能自动安装。否则需要手动在项目中安装这些依赖。

层覆盖与合并

文件覆盖

项目文件 > 层文件:

text
# 层中的文件
layer/app/pages/about.vue      → 层版本

# 项目中的文件(优先)
app/app/pages/about.vue        → 项目版本(使用这个)

覆盖规则:项目自身的文件永远优先。这意味着你可以在不修改层代码的情况下,用项目文件覆盖层中的任何页面、组件或布局。

组件也会被覆盖吗?

是的。如果层定义了 app/components/MyButton.vue,项目中也定义了同名组件,项目版本优先。这在定制第三方层的 UI 时很有用。

配置合并

数组配置会合并,对象配置会深度合并:

ts
// 层配置
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']
})

合并规则详解

配置类型合并行为示例
数组(modulescssplugins追加合并['@nuxt/ui'] + ['@pinia/nuxt']['@nuxt/ui', '@pinia/nuxt']
对象(runtimeConfigapp深度合并层的 runtimeConfig.public 与项目的合并
基本类型(字符串、数字)项目值覆盖层值项目的 ssr: false 覆盖层的 ssr: true

常见陷阱

routeRules 是对象类型,会深度合并。如果层和项目都定义了 /api/** 的规则,会合并而不是覆盖。如果需要完全替换,使用 routeRules 的覆盖模式。

禁用层中的模块

ts
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

实战场景

场景一:项目拆分——管理后台 + 用户端

text
my-app/
├── layers/
│   └── admin/              # 管理后台作为层
│       ├── nuxt.config.ts
│       └── app/
│           ├── pages/
│           │   └── admin/   # /admin/** 路由
│           ├── components/
│           │   └── admin/   # 管理后台专用组件
│           └── layouts/
│               └── admin.vue
├── app/
│   ├── pages/              # 用户端路由
│   └── components/         # 用户端组件
└── nuxt.config.ts

好处:管理后台和用户端在同一个项目中,共享数据库连接、工具函数、类型定义,但页面和组件互不干扰。

场景二:SaaS 多租户主题

ts
// nuxt.config.ts
export default defineNuxtConfig({
  extends: [
    `./layers/themes/${process.env.NUXT_THEME || 'default'}`,
  ],
})
text
layers/
└── themes/
    ├── default/     # 默认主题
    ├── premium/     # 高级主题
    └── enterprise/  # 企业主题

好处:同一套业务逻辑,通过环境变量切换主题层。每个主题层可以覆盖组件样式、布局、页面。

场景三:共享功能层

ts
// nuxt.config.ts
export default defineNuxtConfig({
  extends: [
    './layers/auth',       # 认证层:登录页、中间件、composables
    './layers/analytics',  # 分析层:埋点插件、页面追踪
  ],
})

好处:跨项目复用功能模块。新建项目时 extends 即可获得完整的认证和分析功能。

调试层

查看层的加载情况:

bash
nuxt info

查看所有已注册的层、模块和自动导入。

更详细的调试

bash
# 查看最终合并后的配置
nuxt config show

# 查看层的加载顺序和路径
DEBUG=nuxt:layers nuxt dev

常见问题

路径别名问题

层中的 ~@ 指向使用层的项目,不是层本身。解决方案:

ts
// 层的 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 等工具可能无法访问。解决方案:

  1. 将依赖声明为项目的直接依赖
  2. 使用 install: true 自动安装

层中的 auto-imports 冲突

如果多个层定义了同名的 composable 或组件:

text
layers/admin/app/composables/useAuth.ts    → useAuth()
layers/blog/app/composables/useAuth.ts     → useAuth() ← 冲突!

解决方案:给 composable 加命名空间前缀:

ts
// layers/admin/app/composables/useAdminAuth.ts
// layers/blog/app/composables/useBlogAuth.ts

层中的 server middleware 执行顺序

所有层中的 server/middleware/ 都会生效,按字母顺序执行。如果多个层定义了同名中间件文件(如 auth.ts),只有优先级最高的层的版本会被使用(文件覆盖规则),不是都执行。

层的热更新

本地层(layers/ 目录下)的文件修改会触发热更新。远程层(npm 包、Git 仓库)需要重启开发服务器才能生效。

基于 Nuxt 4 官方文档整理编写