模块创建指南

了解如何创建 Nuxt 模块来集成、增强或扩展任何 Nuxt 应用。

Nuxt 的 configurationhooks 系统使你能够自定义 Nuxt 的各个方面并添加你可能需要的任何集成(Vue 插件、CMS、服务器路由、组件、日志记录等)。

¥Nuxt's configuration and hooks systems make it possible to customize every aspect of Nuxt and add any integration you might need (Vue plugins, CMS, server routes, components, logging, etc.).

Nuxt 模块是使用 nuxi dev 以开发模式启动 Nuxt 或使用 nuxi build 构建生产项目时顺序运行的函数。使用模块,你可以将自定义解决方案封装为 npm 包,并进行适当的测试和共享,而无需在项目中添加不必要的样板代码,也无需更改 Nuxt 本身。

¥Nuxt Modules are functions that sequentially run when starting Nuxt in development mode using nuxi dev or building a project for production with nuxi build. With modules, you can encapsulate, properly test, and share custom solutions as npm packages without adding unnecessary boilerplate to your project, or requiring changes to Nuxt itself.

快速入门

¥Quick Start

我们建议你使用我们的 入门模板 开始使用 Nuxt 模块:

¥We recommend you get started with Nuxt Modules using our starter template:

npm create nuxt -- -t module my-module

这将创建一个 my-module 项目,其中包含开发和发布模块所需的所有样板文件。

¥This will create a my-module project with all the boilerplate necessary to develop and publish your module.

后续步骤:

¥Next steps:

  1. 在你选择的 IDE 中打开 my-module
  2. 使用你常用的包管理器安装依赖
  3. 使用 npm run dev:prepare 准备本地文件以供开发
  4. 请阅读本文档,了解更多关于 Nuxt 模块的信息。

使用 Starter

¥Using the Starter

了解如何使用模块启动器执行基本任务。

¥Learn how to perform basic tasks with the module starter.

观看 Vue School 视频,了解如何 Nuxt 模块入门模板。¥Watch Vue School video about Nuxt module starter template.

如何开发

¥How to Develop

虽然你的模块源代码位于 src 目录中,但在大多数情况下,要开发模块,你需要一个 Nuxt 应用。这就是 playground 目录的作用所在。这是一个你可以自行修改的 Nuxt 应用,它已配置为与你的模块一起运行。

¥While your module source code lives inside the src directory, in most cases, to develop a module, you need a Nuxt application. That's what the playground directory is about. It's a Nuxt application you can tinker with that is already configured to run with your module.

你可以像使用任何 Nuxt 应用一样与 Playground 交互。

¥You can interact with the playground like with any Nuxt application.

  • 使用 npm run dev 启动其开发服务器,当你在 src 目录中对模块进行更改时,它应该会自行重新加载。
  • 使用 npm run dev:build 构建
所有其他 nuxi 命令都可以针对 playground 目录(例如 nuxi <COMMAND> playground)使用。为了方便起见,你可以在 package.json 中声明并引用其他 dev:* 脚本。

如何测试

¥How to Test

模块启动器附带一个基本的测试套件:

¥The module starter comes with a basic test suite:

  • 一个由 ESLint 驱动的 linter,使用 npm run lint 运行它
  • Vitest 驱动的测试运行器,可与 npm run testnpm run test:watch 一起运行
你可以随意扩展此默认测试策略,以更好地满足你的需求。

如何构建

¥How to Build

Nuxt 模块带有由 @nuxt/module-builder 提供的构建器。此构建器无需你进行任何配置,支持 TypeScript,并确保你的资源正确打包以便分发到其他 Nuxt 应用。

¥Nuxt Modules come with their own builder provided by @nuxt/module-builder. This builder doesn't require any configuration on your end, supports TypeScript, and makes sure your assets are properly bundled to be distributed to other Nuxt applications.

你可以通过运行 npm run prepack 来构建你的模块。

¥You can build your module by running npm run prepack.

虽然在某些情况下构建模块很有用,但大多数情况下你无需自行构建:playground 在开发过程中会处理好,发布脚本也会在发布时为你提供支持。

如何发布

¥How to Publish

在将模块发布到 npm 之前,请确保你拥有一个 npmjs.com 账户,并且你已使用 npm login 在本地对其进行身份验证。

虽然你可以通过升级模块版本并使用 npm publish 命令来发布模块,但模块启动器附带一个发布脚本,可帮助你确保将模块的可用版本发布到 npm 等。

¥While you can publish your module by bumping its version and using the npm publish command, the module starter comes with a release script that helps you make sure you publish a working version of your module to npm and more.

要使用发布脚本,请首先提交所有更改(我们建议你遵循 常规提交 以利用自动版本升级和更新日志功能),然后使用 npm run release 运行发布脚本。

¥To use the release script, first, commit all your changes (we recommend you follow Conventional Commits to also take advantage of automatic version bump and changelog update), then run the release script with npm run release.

运行发布脚本时,将发生以下情况:

¥When running the release script, the following will happen:

  • 首先,它将通过以下方式运行你的测试套件:
    • 运行 linter (npm run lint)
    • 运行单元测试、集成测试和端到端测试 (npm run test)
    • 构建模块 (npm run prepack)
  • 然后,如果你的测试套件运行良好,它将通过以下方式发布你的模块:
    • 根据约定式提交更新模块版本并生成更新日志
    • 将模块发布到 npm(为此,模块将重新构建,以确保其更新的版本号在发布的工件中得到考虑)
    • 将代表新发布版本的 git 标签推送到你的 git 远程源
与其他脚本一样,你可以随意调整 package.json 中的默认 release 脚本,以更好地满足你的需求。

开发模块

¥Developing Modules

Nuxt 模块带有各种强大的 API 和模式,允许它们以几乎任何可能的方式修改 Nuxt 应用。本节将教你如何利用这些功能。

¥Nuxt Modules come with a variety of powerful APIs and patterns allowing them to alter a Nuxt application in pretty much any way possible. This section teaches you how to take advantage of those.

模块剖析

¥Module Anatomy

我们可以考虑两种 Nuxt 模块:

¥We can consider two kinds of Nuxt Modules:

  • 已发布的模块在 npm 上分发 - 你可以查看 Nuxt 网站 上的一些社区模块列表。
  • "local" 模块,它们存在于 Nuxt 项目本身中,可以是 在 Nuxt 配置中内联 模块,也可以是 modules 目录 模块的一部分。

无论哪种情况,它们的结构都相似。

¥In either case, their anatomy is similar.

模块定义

¥Module Definition

使用启动器时,模块定义在 src/module.ts 处可用。

模块定义是模块的入口点。当你的模块在 Nuxt 配置中被引用时,Nuxt 会加载它。

¥The module definition is the entry point of your module. It's what gets loaded by Nuxt when your module is referenced within a Nuxt configuration.

从底层来看,Nuxt 模块定义是一个简单的、可能异步的函数,它接受内联用户选项和一个 nuxt 对象来与 Nuxt 交互。

¥At a low level, a Nuxt Module definition is a simple, potentially asynchronous, function accepting inline user options and a nuxt object to interact with Nuxt.

export default function (inlineOptions, nuxt) {
  // You can do whatever you like here..
  console.log(inlineOptions.token) // `123`
  console.log(nuxt.options.dev) // `true` or `false`
  nuxt.hook('ready', async nuxt => {
    console.log('Nuxt is ready')
  })
}

你可以使用 Nuxt 套件 提供的更高级的 defineNuxtModule 助手程序获取此功能的类型提示支持。

¥You can get type-hint support for this function using the higher-level defineNuxtModule helper provided by Nuxt Kit.

import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule((options, nuxt) => {
  nuxt.hook('pages:extend', pages => {
    console.log(`Discovered ${pages.length} pages`)
  })
})

然而,我们不建议使用这种低级函数定义。相反,要定义模块,我们建议使用带有 meta 属性的对象语法来标识你的模块,尤其是在发布到 npm 时。

¥However, we do not recommend using this low-level function definition. Instead, to define a module, we recommend using the object-syntax with meta property to identify your module, especially when publishing to npm.

此辅助程序通过实现模块所需的许多常见模式,使编写 Nuxt 模块变得更加简单,从而保证了未来的兼容性,并提升了模块作者和用户的体验。

¥This helper makes writing Nuxt modules more straightforward by implementing many common patterns needed by modules, guaranteeing future compatibility and improving the experience for both module authors and users.

import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  meta: {
    // Usually the npm package name of your module
    name: '@nuxtjs/example',
    // The key in `nuxt.config` that holds your module options
    configKey: 'sample',
    // Compatibility constraints
    compatibility: {
      // Semver version of supported nuxt versions
      nuxt: '>=3.0.0'
    }
  },
  // Default configuration options for your module, can also be a function returning those
  defaults: {},
  // Shorthand sugar to register Nuxt hooks
  hooks: {},
  // The function holding your module logic, it can be asynchronous
  setup(moduleOptions, nuxt) {
    // ...
  }
})

最终,defineNuxtModule 返回一个带有底层 (inlineOptions, nuxt) 模块签名的封装函数。此封装函数在调用你的 setup 函数之前应用默认值和其他必要步骤:

¥Ultimately defineNuxtModule returns a wrapper function with the lower level (inlineOptions, nuxt) module signature. This wrapper function applies defaults and other necessary steps before calling your setup function:

  • 支持 defaultsmeta.configKey 自动合并模块选项
  • 类型提示和自动类型推断
  • 添加垫片以实现基本的 Nuxt 2 兼容性
  • 确保模块仅使用从 meta.namemeta.configKey 计算的唯一键安装一次。
  • 自动注册 Nuxt 钩子
  • 根据模块元数据自动检查兼容性问题
  • 公开 getOptionsgetMeta 以供 Nuxt 内部使用
  • 只要模块使用来自 @nuxt/kit 最新版本的 defineNuxtModule,就确保向后和向上兼容。
  • 与模块构建器工具集成

运行时目录

¥Runtime Directory

使用启动器时,运行时目录在 src/runtime 处可用。

模块与 Nuxt 配置中的所有内容一样,不包含在应用运行时中。但是,你可能希望模块提供或注入运行时代码到其所安装的应用。这就是运行时目录的功能所在。

¥Modules, like everything in a Nuxt configuration, aren't included in your application runtime. However, you might want your module to provide, or inject runtime code to the application it's installed on. That's what the runtime directory enables you to do.

在运行时目录中,你可以提供任何与 Nuxt 应用相关的资源:

¥Inside the runtime directory, you can provide any kind of assets related to the Nuxt App:

对于 服务器引擎,Nitro:

¥To the server engine, Nitro:

  • API 路线
  • 中间件
  • Nitro 插件

或者,你想要在用户的 Nuxt 应用中注入任何其他类型的资源:

¥Or any other kind of asset you want to inject in users' Nuxt applications:

  • 样式表
  • 3D 模型
  • 图片
  • 等等

然后,你将能够从 模块定义 将所有这些资源注入到应用中。

¥You'll then be able to inject all those assets inside the application from your module definition.

了解更多关于 the recipes section 中的资源注入的信息。
已发布的模块无法利用其运行时目录中的资源自动导入功能。相反,它们必须从 #imports 或类似组件中显式导入它们。:br
事实上,出于性能原因,node_modules(已发布模块最终将存储的位置)内的文件未启用自动导入。:br
如果你使用模块启动器,你的 Playground 中也不会启用自动导入功能。¥Published modules cannot leverage auto-imports for assets within their runtime directory. Instead, they have to import them explicitly from #imports or alike.

Indeed, auto-imports are not enabled for files within node_modules (the location where a published module will eventually live) for performance reasons.

If you are using the module starter, auto-imports will not be enabled in your playground either.

工具

¥Tooling

模块附带一套官方工具来帮助你进行开发。

¥Modules come with a set of first-party tools to help you with their development.

@nuxt/module-builder

Nuxt 模块构建器 是一款零配置构建工具,可处理构建和交付模块的所有繁重工作。它确保模块构建工件与 Nuxt 应用正确兼容。

¥Nuxt Module Builder is a zero-configuration build tool taking care of all the heavy lifting to build and ship your module. It ensures proper compatibility of your module build artifact with Nuxt applications.

@nuxt/kit

Nuxt 套件 提供了一些可组合实用程序,帮助你的模块与 Nuxt 应用交互。建议尽可能使用 Nuxt Kit 实用程序而不是手动替代方案,以确保模块具有更好的兼容性和代码可读性。

¥Nuxt Kit provides composable utilities to help your module interact with Nuxt applications. It's recommended to use Nuxt Kit utilities over manual alternatives whenever possible to ensure better compatibility and code readability of your module.

Read more in Docs > Guide > Going Further > Kit.

@nuxt/test-utils

Nuxt 测试实用程序 是一组实用程序,可帮助你在模块测试中设置和运行 Nuxt 应用。

¥Nuxt Test Utils is a collection of utilities to help set up and run Nuxt applications within your module tests.

Recipes

在这里找到编写模块的常用模式。

¥Find here common patterns used to author modules.

修改 Nuxt 配置

¥Altering Nuxt Configuration

Nuxt 配置可以通过模块读取和修改。以下是启用实验性功能的模块示例。

¥Nuxt configuration can be read and altered by modules. Here's an example of a module enabling an experimental feature.

import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    // We create the `experimental` object if it doesn't exist yet
    nuxt.options.experimental ||= {}
    nuxt.options.experimental.componentIslands = true
  }
})

当你需要处理更复杂的配置更改时,你应该考虑使用 defu

¥When you need to handle more complex configuration alterations, you should consider using defu.

观看 Vue School 的视频,了解如何修改 Nuxt 配置。¥Watch Vue School video about altering Nuxt configuration.

向运行时暴露选项

¥Exposing Options to Runtime

由于模块不是应用运行时的一部分,因此它们的选项也不是。但是,在许多情况下,你可能需要在运行时代码中访问其中一些模块选项。我们建议使用 Nuxt 的 runtimeConfig 公开所需的配置。

¥Because modules aren't part of the application runtime, their options aren't either. However, in many cases, you might need access to some of these module options within your runtime code. We recommend exposing the needed config using Nuxt's runtimeConfig.

import { defineNuxtModule } from '@nuxt/kit'
import { defu } from 'defu'

export default defineNuxtModule({
  setup (options, nuxt) {
    nuxt.options.runtimeConfig.public.myModule = defu(nuxt.options.runtimeConfig.public.myModule, {
      foo: options.foo
    })
  }
})

请注意,我们使用 defu 来扩展用户提供的公共运行时配置,而不是覆盖它。

¥Note that we use defu to extend the public runtime configuration the user provides instead of overwriting it.

然后,你可以像任何其他运行时配置一样,在插件、组件或应用中访问你的模块选项:

¥You can then access your module options in a plugin, component, the application like any other runtime configuration:

const options = useRuntimeConfig().public.myModule
注意不要在公共运行时配置中暴露任何敏感的模块配置,例如私有 API 密钥,因为它们最终会包含在公共 bundle 中。¥Be careful not to expose any sensitive module configuration on the public runtime config, such as private API keys, as they will end up in the public bundle.
Read more in Docs > Guide > Going Further > Runtime Config.
观看 Vue School 的视频,了解如何传递和公开 Nuxt 模块选项。¥Watch Vue School video about passing and exposing Nuxt module options.

使用 addPlugin 注入插件

¥Injecting Plugins With addPlugin

插件是模块添加运行时逻辑的常用方法。你可以使用 addPlugin 实用程序从模块中注册它们。

¥Plugins are a common way for a module to add runtime logic. You can use the addPlugin utility to register them from your module.

import { defineNuxtModule, addPlugin, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    // Create resolver to resolve relative paths
    const resolver = createResolver(import.meta.url)

    addPlugin(resolver.resolve('./runtime/plugin'))
  }
})
Read more in Docs > Guide > Going Further > Kit.

使用 addComponent 注入 Vue 组件 addComponent

¥Injecting Vue Components With addComponent

如果你的模块需要提供 Vue 组件,你可以使用 addComponent 实用程序将它们添加为自动导入,以便 Nuxt 解析。

¥If your module should provide Vue components, you can use the addComponent utility to add them as auto-imports for Nuxt to resolve.

import { defineNuxtModule, addComponent } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    const resolver = createResolver(import.meta.url)

    // From the runtime directory
    addComponent({
      name: 'MySuperComponent', // name of the component to be used in vue templates
      export: 'MySuperComponent', // (optional) if the component is a named (rather than default) export
      filePath: resolver.resolve('runtime/components/MySuperComponent.vue')
    })

    // From a library
    addComponent({
      name: 'MyAwesomeComponent', // name of the component to be used in vue templates
      export: 'MyAwesomeComponent', // (optional) if the component is a named (rather than default) export
      filePath: '@vue/awesome-components'
    })
  }
})

或者,你可以使用 addComponentsDir 添加整个目录。

¥Alternatively, you can add an entire directory by using addComponentsDir.

import { defineNuxtModule, addComponentsDir } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    const resolver = createResolver(import.meta.url)

    addComponentsDir({
      path: resolver.resolve('runtime/components')
    })
  }
})

使用 addImportsaddImportsDir 注入可组合项

¥Injecting Composables With addImports and addImportsDir

如果你的模块需要提供可组合项,你可以使用 addImports 实用程序将它们添加为自动导入,以便 Nuxt 解析。

¥If your module should provide composables, you can use the addImports utility to add them as auto-imports for Nuxt to resolve.

import { defineNuxtModule, addImports, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    const resolver = createResolver(import.meta.url)

    addImports({
      name: 'useComposable', // name of the composable to be used
      as: 'useComposable',
      from: resolver.resolve('runtime/composables/useComposable') // path of composable
    })
  }
})

或者,你可以使用 addImportsDir 添加整个目录。

¥Alternatively, you can add an entire directory by using addImportsDir.

import { defineNuxtModule, addImportsDir, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    const resolver = createResolver(import.meta.url)

    addImportsDir(resolver.resolve('runtime/composables'))
  }
})

使用 addServerHandler 注入服务器路由

¥Injecting Server Routes With addServerHandler

import { defineNuxtModule, addServerHandler, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    const resolver = createResolver(import.meta.url)

    addServerHandler({
      route: '/api/hello',
      handler: resolver.resolve('./runtime/server/api/hello/index.get')
    })
  }
})

你还可以添加动态服务器路由:

¥You can also add a dynamic server route:

import { defineNuxtModule, addServerHandler, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    const resolver = createResolver(import.meta.url)

    addServerHandler({
      route: '/api/hello/:name',
      handler: resolver.resolve('./runtime/server/api/hello/[name].get')
    })
  }
})

注入其他资源

¥Injecting Other Assets

如果你的模块需要提供其他类型的资源,也可以注入它们。以下是一个简单的示例模块,它通过 Nuxt 的 css 数组注入样式表。

¥If your module should provide other kinds of assets, they can also be injected. Here's a simple example module injecting a stylesheet through Nuxt's css array.

import { defineNuxtModule, addPlugin, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    nuxt.options.css.push(resolver.resolve('./runtime/style.css'))
  }
})

还有更高级的方法,通过 NitropublicAssets 选项公开资源文件夹:

¥And a more advanced one, exposing a folder of assets through Nitro's publicAssets option:

import { defineNuxtModule, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    nuxt.hook('nitro:config', async (nitroConfig) => {
      nitroConfig.publicAssets ||= []
      nitroConfig.publicAssets.push({
        dir: resolver.resolve('./runtime/public'),
        maxAge: 60 * 60 * 24 * 365 // 1 year
      })
    })
  }
})

在你的模块中使用其他模块

¥Using Other Modules in Your Module

如果你的模块依赖于其他模块,你可以使用 Nuxt Kit 的 installModule 实用程序添加它们。例如,如果你想在模块中使用 Nuxt Tailwind,可以按如下方式添加:

¥If your module depends on other modules, you can add them by using Nuxt Kit's installModule utility. For example, if you wanted to use Nuxt Tailwind in your module, you could add it as below:

import { defineNuxtModule, createResolver, installModule } from '@nuxt/kit'

export default defineNuxtModule<ModuleOptions>({
  async setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    // We can inject our CSS file which includes Tailwind's directives
    nuxt.options.css.push(resolver.resolve('./runtime/assets/styles.css'))

    await installModule('@nuxtjs/tailwindcss', {
      // module configuration
      exposeConfig: true,
      config: {
        darkMode: 'class',
        content: {
          files: [
            resolver.resolve('./runtime/components/**/*.{vue,mjs,ts}'),
            resolver.resolve('./runtime/*.{mjs,js,ts}')
          ]
        }
      }
    })
  }
})

使用 Hooks

¥Using Hooks

生命周期钩子 允许你扩展 Nuxt 的几乎所有方面。模块可以通过编程方式或通过其定义中的 hooks 映射连接到模块。

¥Lifecycle hooks allow you to expand almost every aspect of Nuxt. Modules can hook to them programmatically or through the hooks map in their definition.

import { defineNuxtModule, addPlugin, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  // Hook to the `app:error` hook through the `hooks` map
  hooks: {
    'app:error': (err) => {
      console.info(`This error happened: ${err}`);
    }
  },
  setup (options, nuxt) {
    // Programmatically hook to the `pages:extend` hook
    nuxt.hook('pages:extend', (pages) => {
      console.info(`Discovered ${pages.length} pages`);
    })
  }
})
Read more in Docs > API > Advanced > Hooks.
观看 Vue School 的视频,了解如何在模块中使用 Nuxt 生命周期钩子。¥Watch Vue School video about using Nuxt lifecycle hooks in modules.
Module cleanup

如果你的模块打开、处理或启动了监视器,则应在 Nuxt 生命周期结束时将其关闭。close 钩子可用于此操作。 ```ts import { defineNuxtModule } from '@nuxt/kit'export default defineNuxtModule({ setup (options, nuxt) { nuxt.hook('close', async nuxt => { // Your custom code here }) } })
::

<a id="adding-templatesvirtual-files"></a>

#### 添加模板/虚拟文件

¥Adding Templates/Virtual Files

如果你需要添加可导入用户应用的虚拟文件,可以使用 `addTemplate` 实用程序。

¥If you need to add a virtual file that can be imported into the user's app, you can use the `addTemplate` utility.

```ts
import { defineNuxtModule, addTemplate } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    // The file is added to Nuxt's internal virtual file system and can be imported from '#build/my-module-feature.mjs'
    addTemplate({
      filename: 'my-module-feature.mjs',
      getContents: () => 'export const myModuleFeature = () => "hello world !"'
    })
  }
})
为此,你可以使用新的 addServerTemplate 和 配置选项。¥For the server, you should use the addServerTemplate utility instead.
import { defineNuxtModule, addServerTemplate } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    // The file is added to Nitro's virtual file system and can be imported in the server code from 'my-server-module.mjs'
    addServerTemplate({
      filename: 'my-server-module.mjs',
      getContents: () => 'export const myServerModule = () => "hello world !"'
    })
  }
})

添加类型声明

¥Adding Type Declarations你可能还需要向用户的项目添加类型声明(例如,扩充 Nuxt 接口或提供你自己的全局类型)。为此,可以使用 addTypeTemplate 模式:¥You might also want to add a type declaration to the user's project (for example, to augment a Nuxt interface or provide a global type of your own). For this, Nuxt provides the addTypeTemplate utility that both writes a template to the disk and adds a reference to it in the generated nuxt.d.ts file.如果你的模块需要扩充 Nuxt 处理的类型,你可以使用 addTypeTemplate 执行以下操作:¥If your module should augment types handled by Nuxt, you can use addTypeTemplate to perform this operation:
import { defineNuxtModule, addTemplate, addTypeTemplate } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    addTypeTemplate({
      filename: 'types/my-module.d.ts',
      getContents: () => `// Generated by my-module
        interface MyModuleNitroRules {
          myModule?: { foo: 'bar' }
        }
        declare module 'nitro/types' {
          interface NitroRouteRules extends MyModuleNitroRules {}
          interface NitroRouteConfig extends MyModuleNitroRules {}
        }
        export {}`
    })
  }
})
如果你需要更精细的控制,你可以使用 prepare:types 钩子注册一个回调函数来注入你的类型。¥If you need more granular control, you can use the prepare:types hook to register a callback that will inject your types.
const template = addTemplate({ /* template options */ })
nuxt.hook('prepare:types', ({ references }) => {
  references.push({ path: template.dst })
})
更新模板
¥Updating Templates如果你需要更新模板/虚拟文件,可以像这样使用 updateTemplates 实用程序:¥If you need to update your templates/virtual files, you can leverage the updateTemplates utility like this :
nuxt.hook('builder:watch', async (event, path) => {
  if (path.includes('my-module-feature.config')) {
    // This will reload the template that you registered
    updateTemplates({ filter: t => t.filename === 'my-module-feature.mjs' })
  }
})

测试

¥Testing测试有助于确保你的模块在各种设置下都能正常工作。在本节中了解如何对你的模块执行各种测试。¥Testing helps ensuring your module works as expected given various setup. Find in this section how to perform various kinds of tests against your module.

单元与集成

¥Unit and Integration
我们仍在讨论和探索如何简化 Nuxt 模块的单元测试和集成测试。:br
Check out this RFC to join the conversation

端到端

¥End to EndNuxt 测试实用程序 是帮助你以端到端方式测试模块的首选库。以下是使用方法:¥Nuxt Test Utils is the go-to library to help you test your module in an end-to-end way. Here's the workflow to adopt with it:
  1. 创建一个 Nuxt 应用,用作 test/fixtures/* 中的 "fixture"。
  2. 在测试文件中使用此 Fixture 设置 Nuxt
  3. 使用 @nuxt/test-utils 中的实用程序与 Fixture 交互(例如,获取页面)
  4. 执行与此 Fixture(例如 "HTML 包含……")相关的检查
  5. 重复
实际操作中,该夹具:¥In practice, the fixture:
test/fixtures/ssr/nuxt.config.ts
// 1. Create a Nuxt application to be used as a "fixture"
import MyModule from '../../../src/module'

export default defineNuxtConfig({
  ssr: true,
  modules: [
    MyModule
  ]
})
及其测试:¥And its test:
test/rendering.ts
import { describe, it, expect } from 'vitest'
import { fileURLToPath } from 'node:url'
import { setup, $fetch } from '@nuxt/test-utils/e2e'

describe('ssr', async () => {
  // 2. Setup Nuxt with this fixture inside your test file
  await setup({
    rootDir: fileURLToPath(new URL('./fixtures/ssr', import.meta.url)),
  })

  it('renders the index page', async () => {
    // 3. Interact with the fixture using utilities from `@nuxt/test-utils`
    const html = await $fetch('/')

    // 4. Perform checks related to this fixture
    expect(html).toContain('<div>ssr</div>')
  })
})

// 5. Repeat
describe('csr', async () => { /* ... */ })
the module starter 上提供了此类工作流程的示例。

使用 Playground 和外部进行手动 QA

¥Manual QA With Playground and Externally在开发模块时,使用 Nuxt Playground 应用来测试模块非常有用。模块启动器集成了一个用于此目的的钩子。¥Having a playground Nuxt application to test your module when developing it is really useful. The module starter integrates one for that purpose.你可以在本地使用其他 Nuxt 应用(不属于你的模块存储库的应用)测试你的模块。为此,你可以使用 npm pack 命令或等效的包管理器从模块创建 tarball。然后在你的测试项目中,你可以将模块添加到 package.json 包中,如下所示:"my-module": "file:/path/to/tarball.tgz"。¥You can test your module with other Nuxt applications (applications that are not part of your module repository) locally. To do so, you can use npm pack command, or your package manager equivalent, to create a tarball from your module. Then in your test project, you can add your module to package.json packages as: "my-module": "file:/path/to/tarball.tgz".之后,你应该能够像在任何常规项目中一样引用 my-module。¥After that, you should be able to reference my-module like in any regular project.

最佳实践

¥Best Practices能力越大,责任越大。虽然模块功能强大,但在编写模块时需要牢记以下一些最佳实践,以确保应用性能和良好的开发者体验。¥With great power comes great responsibility. While modules are powerful, here are some best practices to keep in mind while authoring modules to keep applications performant and developer experience great.

异步模块

¥Async Modules正如我们所见,Nuxt 模块可以是异步的。例如,你可能想要开发一个需要获取某些 API 或调用异步函数的模块。¥As we've seen, Nuxt Modules can be asynchronous. For example, you may want to develop a module that needs fetching some API or calling an async function.但是,请谨慎使用异步行为,因为 Nuxt 会等待你的模块设置完成后,才会转到下一个模块并启动开发服务器、构建过程等。建议你将耗时的逻辑推迟到 Nuxt hooks 中。¥However, be careful with asynchronous behaviors as Nuxt will wait for your module to setup before going to the next module and starting the development server, build process, etc. Prefer deferring time-consuming logic to Nuxt hooks.
如果你的模块设置时间超过 1 秒,Nuxt 会发出警告。¥If your module takes more than 1 second to setup, Nuxt will emit a warning about it.

始终为暴露的接口添加前缀

¥Always Prefix Exposed InterfacesNuxt 模块应为任何公开的配置、插件、API、可组合项或组件提供明确的前缀,以避免与其他模块和内部组件冲突。¥Nuxt Modules should provide an explicit prefix for any exposed configuration, plugin, API, composable, or component to avoid conflict with other modules and internals.理想情况下,你应该在它们前面加上模块名称(例如,如果你的模块名为 nuxt-foo,则应暴露 <FooButton>useFooBar(),而不是 <Button>useBar())。¥Ideally, you should prefix them with your module's name (e.g. if your module is called nuxt-foo, expose <FooButton> and useFooBar() and not<Button> and useBar()).

兼容 TypeScript

¥Be TypeScript FriendlyNuxt 拥有一流的 TypeScript 集成,可提供最佳的开发者体验。¥Nuxt has first-class TypeScript integration for the best developer experience.即使不直接使用 TypeScript,公开类型并使用 TypeScript 开发模块也能让用户受益。¥Exposing types and using TypeScript to develop modules benefits users even when not using TypeScript directly.

避免 CommonJS 语法

¥Avoid CommonJS SyntaxNuxt 依赖于原生 ESM。请阅读 原生 ES 模块 了解更多信息。¥Nuxt relies on native ESM. Please read Native ES Modules for more information.

文档模块使用

¥Document Module Usage请考虑在自述文件中记录模块使用情况:¥Consider documenting module usage in the readme file:
  • 为什么要使用这个模块?
  • 如何使用此模块?
  • 此模块的作用是什么?
链接到集成网站和文档始终是一个好主意。¥Linking to the integration website and documentation is always a good idea.

提供 StackBlitz 演示或样板

¥Provide a StackBlitz Demo or Boilerplate最好使用你的模块和你添加到模块自述文件中的 StackBlitz 进行最小复制。¥It's a good practice to make a minimal reproduction with your module and StackBlitz that you add to your module readme.这不仅为模块的潜在用户提供了一种快速简便的方法来试验模块,也为他们提供了一种简单的方法来构建最小的复制版本,以便在遇到问题时发送给你。¥This not only provides potential users of your module a quick and easy way to experiment with the module but also an easy way for them to build minimal reproductions they can send you when they encounter issues.

不要使用特定的 Nuxt 版本进行推广

¥Do Not Advertise With a Specific Nuxt VersionNuxt、Nuxt Kit 和其他新工具在设计时都考虑了向前和向后兼容性。¥Nuxt, Nuxt Kit, and other new toolings are made to have both forward and backward compatibility in mind.请使用 "X 代表 Nuxt" 而不是 "X 代表 Nuxt 3",以避免生态系统碎片化,并优先使用 meta.compatibility 来设置 Nuxt 版本约束。¥Please use "X for Nuxt" instead of "X for Nuxt 3" to avoid fragmentation in the ecosystem and prefer using meta.compatibility to set Nuxt version constraints.

坚持初始默认值

¥Stick With Starter Defaults模块启动器附带一组默认的工具和配置(例如 ESLint 配置)。如果你计划开源模块,坚持这些默认设置可确保你的模块与其他 社区模块 模块共享一致的编码风格,从而更轻松地供其他人贡献代码。¥The module starter comes with a default set of tools and configurations (e.g. ESLint configuration). If you plan on open-sourcing your module, sticking with those defaults ensures your module shares a consistent coding style with other community modules out there, making it easier for others to contribute.

生态系统

¥EcosystemNuxt 模块生态系统 每月 NPM 下载量超过 1500 万次,并提供扩展功能以及与各种工具的集成。你可以成为这个生态系统的一部分!¥Nuxt Module ecosystem represents more than 15 million monthly NPM downloads and provides extended functionalities and integrations with all sort of tools. You can be part of this ecosystem!
观看 Vue School 视频,了解 Nuxt 模块类型。¥Watch Vue School video about Nuxt module types.

模块类型

¥Module Types官方模块是以 @nuxt/ 为前缀(作用域)的模块(例如 @nuxt/content)。它们由 Nuxt 团队开发并积极维护。与框架一样,我们非常欢迎社区的贡献,帮助我们改进它!¥Official modules are modules prefixed (scoped) with @nuxt/ (e.g. @nuxt/content). They are made and maintained actively by the Nuxt team. Like with the framework, contributions from the community are more than welcome to help make them better!社区模块是以 @nuxtjs/ 为前缀(作用域)的模块(例如 @nuxtjs/tailwindcss)。它们是由社区成员开发和维护的成熟模块。再次欢迎任何人的贡献。¥Community modules are modules prefixed (scoped) with @nuxtjs/ (e.g. @nuxtjs/tailwindcss). They are proven modules made and maintained by community members. Again, contributions are welcome from anyone.第三方和其他社区模块通常以 nuxt- 为前缀。任何人都可以创建它们,使用此前缀可以使这些模块在 npm 上被发现。这是起草和尝试想法的最佳起点!¥Third party and other community modules are modules (often) prefixed with nuxt-. Anyone can make them, using this prefix allows these modules to be discoverable on npm. This is the best starting point to draft and try an idea!私有模块或个人模块是为你自己的用例或公司创建的模块。它们无需遵循任何命名规则即可与 Nuxt 配合使用,并且通常被归入 npm 组织(例如 @my-company/nuxt-auth)。¥Private or personal modules are modules made for your own use case or company. They don't need to follow any naming rules to work with Nuxt and are often seen scoped under an npm organization (e.g. @my-company/nuxt-auth)

列出你的社区模块

¥Listing Your Community Module欢迎任何社区模块在 模块列表 上架。用于列出 在 nuxt/modules 中提交问题 仓库。Nuxt 团队可以帮助你在上架之前应用最佳实践。¥Any community modules are welcome to be listed on the module list. To be listed, open an issue in the nuxt/modules repository. The Nuxt team can help you to apply best practices before listing.

连接 nuxt-modules@nuxtjs/

¥Joining nuxt-modules and @nuxtjs/将你的模块迁移到 nuxt-modules,总会有人提供帮助,这样,我们就可以齐心协力,打造一个完美的解决方案。¥By moving your modules to nuxt-modules, there is always someone else to help, and this way, we can join forces to make one perfect solution.如果你有一个已发布且可运行的模块,并希望将其转移到 nuxt-modules在 nuxt/modules 中提交问题。¥If you have an already published and working module, and want to transfer it to nuxt-modules, open an issue in nuxt/modules.加入 nuxt-modules 后,我们可以在 @nuxtjs/ 范围内重命名你的社区模块,并为其文档提供子域名(例如 my-module.nuxtjs.org)。¥By joining nuxt-modules we can rename your community module under the @nuxtjs/ scope and provide a subdomain (e.g. my-module.nuxtjs.org) for its documentation.