跳到主要内容

编写 Nuxt Layers

Nuxt 提供了一个强大的系统,允许你扩展默认文件、配置等等。

Nuxt layers 是一个强大的特性,你可以用来在 monorepo 中、或从 git 仓库或 npm 包中,共享和复用部分 Nuxt 应用。layer 的结构与标准的 Nuxt 应用几乎完全相同,这使得它们易于编写和维护。

一个最小的 Nuxt layer 目录应该包含一个 nuxt.config.ts 文件,以表明它是一个 layer。

base/nuxt.config.ts
export default defineNuxtConfig({})

此外,layer 目录中的某些其它文件会被 Nuxt 自动扫描,并被扩展该 layer 的项目所使用。

基础示例

nuxt.config.ts
export default defineNuxtConfig({
  extends: [
    './base',
  ],
})

Layer 优先级

当从多个 layer 扩展时,理解覆盖顺序很重要。当它们定义了相同的文件或组件时,优先级更高的 layer 会覆盖优先级更低的 layer。

优先级从高到低为:

  1. 你的项目文件 - 始终拥有最高的优先级
  2. 来自 ~~/layers 目录的自动扫描 layer - 按字母顺序排序(Z 的优先级高于 A)
  3. extends 配置中的 layer - 第一个条目的优先级高于第二个

何时使用哪种

  • extends - 用于外部依赖(npm 包、远程仓库)或项目目录之外的 layer
  • ~~/layers 目录 - 用于作为你项目一部分的本地 layer
如果你需要控制自动扫描 layer 的顺序,可以给它们加上数字前缀:~/layers/1.z-layer~/layers/2.a-layer。这样 2.a-layer 的优先级会高于 1.z-layer

示例

nuxt.config.ts
export default defineNuxtConfig({
  extends: [
    // 项目外的本地 layer
    '../base',
    // NPM 包
    '@my-themes/awesome',
    // 远程仓库
    'github:my-themes/awesome#v1',
  ],
})

如果你同时拥有 ~~/layers/custom,优先级顺序是:

  • 你的项目文件(最高)
  • ~~/layers/custom
  • ../base
  • @my-themes/awesome
  • github:my-themes/awesome#v1(最低)

这意味着你的项目文件会覆盖任何 layer,而 ~~/layers/custom 会覆盖 extends 中的任何内容。

启动模板

要开始,你可以使用 nuxt/starter/layer 模板 初始化一个 layer。这会创建一个你可以继续构建的基础结构。在终端中执行此命令来开始:

Terminal
npm create nuxt -- --template layer nuxt-layer

按照 README 中的说明进行后续步骤。

发布 Layers

你可以通过远程源或 npm 包来发布和共享 layer。

Git 仓库

你可以使用 git 仓库来共享你的 Nuxt layer。一些例子:

nuxt.config.ts
export default defineNuxtConfig({
  extends: [
    // GitHub 远程源
    'github:username/repoName',
    // GitHub 位于 /base 目录下的远程源
    'github:username/repoName/base',
    // 使用 dev 分支的 GitHub 远程源
    'github:username/repoName#dev',
    // 使用 v1.0.0 tag 的 GitHub 远程源
    'github:username/repoName#v1.0.0',
    // GitLab 远程源示例
    'gitlab:username/repoName',
    // Bitbucket 远程源示例
    'bitbucket:username/repoName',
  ],
})
我们建议将你的 layer 内容作为 npm 包(公开或私有,例如通过 GitHub Packages)发布,而不是依赖远程 layer。或者,你可以直接将远程 git URL 作为依赖添加
如果你想扩展一个私有的远程源,你需要添加环境变量 GIGET_AUTH=<token> 来提供一个 token。
如果你想从一个自托管的 GitHub 或 GitLab 实例扩展远程源,你需要用 GIGET_GITHUB_URL=<url>GIGET_GITLAB_URL=<url> 环境变量提供它的 URL——或者直接在 nuxt.configauth 选项配置
请记住,如果你作为 layer 扩展一个远程源,你将无法在 Nuxt 之外访问它的依赖。例如,如果远程 layer 依赖一个 eslint 插件,这无法在你的 eslint 配置中使用。这是因为这些依赖位于一个特殊的位置(node_modules/.c12/layer_name/node_modules/),你的包管理器无法访问。
使用 git 远程源时,如果某个 layer 有 npm 依赖且你希望安装它们,你可以通过在 layer 选项中指定 install: true 来实现。
nuxt.config.ts
export default defineNuxtConfig({
  extends: [
    ['github:username/repoName', { install: true }],
  ],
})

npm 包

你可以将 Nuxt layers 作为 npm 包发布,其中包含你想要扩展的文件和依赖。这允许你与他人共享你的配置、在多个项目中使用它,或者私有地使用它。

要从 npm 包扩展,你需要确保该模块已发布到 npm,并作为 devDependency 安装在用户的项目中。然后你就可以使用该模块名来扩展当前的 nuxt 配置:

nuxt.config.ts
export default defineNuxtConfig({
  extends: [
    // 带作用域的 Node 模块
    '@scope/moduleName',
    // 或者仅仅是模块名
    'moduleName',
  ],
})

要将一个 layer 目录作为 npm 包发布,你需要确保 package.json 中填写了正确的属性。这能确保在发布包时包含相应的文件。

package.json
{
  "name": "my-theme",
  "version": "1.0.0",
  "type": "module",
  "main": "./nuxt.config.ts",
  "dependencies": {},
  "devDependencies": {
    "nuxt": "^3.0.0"
  }
}

确保 layer 中导入的任何依赖都被显式添加dependencies 中。nuxt 依赖,以及任何仅用于在发布前测试 layer 的依赖,应该保留在 devDependencies 字段中。

现在你可以继续将模块发布到 npm,无论是公开还是私有。

当将 layer 作为私有 npm 包发布时,你需要确保已登录,以向 npm 进行身份验证来下载该 node 模块。

提示

具名 Layer 别名

自动扫描的 layer(来自你的 ~~/layers 目录)会自动创建别名。例如,你可以通过 #layers/test 访问你的 ~~/layers/test layer。

如果你想为其它 layer 创建具名 layer 别名,你可以在该 layer 的配置中指定一个名称。

nuxt.config.ts
export default defineNuxtConfig({
  $meta: {
    name: 'example',
  },
})

这会生成一个指向你 layer 的 #layers/example 别名。

相对路径与别名

在 layer 的组件和组合式函数中使用全局别名(如 ~/@/)导入时,请注意这些别名是相对于用户项目路径解析的。作为变通方案,你可以使用相对路径来导入它们,或者使用具名 layer 别名。

同样,在 layer 的 nuxt.config 文件中使用相对路径时(嵌套的 extends 除外),它们是相对于用户的项目解析的,而不是相对于 layer。作为变通方案,在 nuxt.config 中使用完整的解析路径:

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/main.css'),
  ],
})

从 Layers 中禁用模块 v4.3

当扩展一个 layer 时,你可能想禁用它包含的某些模块。你可以通过将模块的 config key 设为 false 来做到这一点,在你的 Nuxt 配置中。

nuxt.config.ts
export default defineNuxtConfig({
  extends: ['./base-layer'],
  // 通过将它们的 config key 设为 false 来禁用 layer 中的模块
  image: false, // 禁用 @nuxt/image
  pinia: false, // 禁用 @pinia/nuxt
})
config key 由每个模块定义。常见的例子包括 @nuxt/imageimage@pinia/nuxtpinia,以及 @nuxt/contentcontent。请查阅模块的文档了解它特定的 config key。

这在以下情况很有用:

  • 一个 layer 包含了你的项目中不需要的模块
  • 你想使用与该 layer 提供的不同的实现
  • 你需要在特定环境中禁用分析或其它模块
你也可以使用这种方式在你自己的项目中禁用模块——而不只是来自 layer 的模块。将模块的 config key 设为 false 会阻止其 setup 函数运行,同时仍然为该模块生成类型。

Nuxt 模块对多 Layer 的支持

你可以使用 Nuxt Kit 中的 getLayerDirectories 工具函数,为你的模块支持自定义的多 layer 处理。

modules/my-module.ts
import { defineNuxtModule, getLayerDirectories } from 'nuxt/kit'

export default defineNuxtModule({
  setup (_options, nuxt) {
    const layerDirs = getLayerDirectories()

    for (const [index, layer] of layerDirs.entries()) {
      console.log(`Layer ${index}:`)
      console.log(`  Root: ${layer.root}`)
      console.log(`  App: ${layer.app}`)
      console.log(`  Server: ${layer.server}`)
      console.log(`  Pages: ${layer.appPages}`)
      // ... 其它目录
    }
  },
})

注意:

  • 数组中靠前的项优先级更高,会覆盖靠后的项
  • 用户的项目是数组中的第一项

深入了解

配置加载和 extends 支持由 unjs/c12 处理,使用 unjs/defu 合并,远程 git 源由 unjs/giget 支持。查看文档和源代码以了解更多信息。

在 GitHub 上查看我们为 layer 支持带来更多改进的持续推进。