遵循最佳实践

遵循这些指南,构建高性能且易维护的 Nuxt 模块。

能力越大,责任越大。虽然模块功能强大,但在编写模块时,请牢记以下一些最佳实践,以保持应用的高性能和良好的开发体验。

处理异步初始化

如前所述,Nuxt 模块可以是异步的。例如,你可能需要开发一个模块,需要调用某个 API 或异步函数。

但是,请谨慎使用异步行为,因为 Nuxt 会等待你的模块完成初始化后,才会继续加载下一个模块并启动开发服务器、构建流程等。建议将耗时较长的逻辑延迟到 Nuxt 钩子中执行。

如果你的模块初始化时间超过 1 秒,Nuxt 会发出警告。

给导出加前缀

Nuxt 模块应为任何暴露的配置、插件、API、组合式函数、组件或服务器路由提供明确的前缀,以避免与其他模块、Nuxt 内部或用户自定义代码发生冲突。

理想情况下,以你的模块名作为前缀。例如,如果你的模块名为 nuxt-foo

类型❌ 避免✅ 推荐
组件<Button><Modal><FooButton><FooModal>
组合式函数useData()useModal()useFooData()useFooModal()
服务器路由/api/track/api/data/api/_foo/track/api/_foo/data

服务器路由

这对于服务器路由尤为重要,因为像 /api/auth/api/login/api/user 这样常见的路径很可能已经被应用占用。

请使用基于模块名的唯一前缀:

  • /api/_foo/...(使用下划线前缀)
  • /_foo/...(用于非 API 路由)

使用生命周期钩子

当你的模块需要执行一次性初始化任务(比如生成配置文件、设置数据库或安装依赖)时,应使用生命周期钩子,而不是在主 setup 函数中执行这些逻辑。

import { addServerHandler, defineNuxtModule } from 'nuxt/kit'
import semver from 'semver'

export default defineNuxtModule({
  meta: {
    name: 'my-database-module',
    version: '1.0.0',
  },
  async onInstall (nuxt) {
    // 一次性初始化:创建数据库 schema,生成配置文件等
    await generateDatabaseConfig(nuxt.options.rootDir)
  },
  async onUpgrade (nuxt, options, previousVersion) {
    // 处理版本迁移
    if (semver.lt(previousVersion, '1.0.0')) {
      await migrateLegacyData()
    }
  },
  setup (options, nuxt) {
    // 常规初始化逻辑,每次构建时执行
    addServerHandler({ /* ... */ })
  },
})

这种模式可避免每次构建时重复执行不必要的工作,提升开发体验。更多细节请参阅 生命周期钩子文档

确保 TypeScript 友好

Nuxt 拥有一流的 TypeScript 集成,能够提供最佳的开发体验。

暴露类型并使用 TypeScript 开发模块,即使用户不直接使用 TypeScript,也能从中受益。

使用 ESM 语法

Nuxt 依赖原生 ESM。更多信息请阅读 原生 ES 模块

书写模块文档

建议在 README 文件中记录模块的使用说明:

  • 为什么使用该模块?
  • 如何使用该模块?
  • 该模块具体做了什么?

链接到集成网站和文档始终是个好主意。

提供演示示例

推荐使用 StackBlitz 做一个包含你模块的最小复现示例,并将其添加到模块的 README 中。

这不仅为潜在用户提供了快速试用模块的途径,也方便他们在遇到问题时构建最小复现示例反馈给你。

保持版本无关性

Nuxt、Nuxt Kit 以及其他新工具均考虑了向前兼容和向后兼容。

请使用 “X for Nuxt” 而非 “X for Nuxt 3”,以避免生态系统碎片化,且优先使用 meta.compatibility 来设置 Nuxt 版本约束。

遵循 Starter 约定

模块启动模板带有一套默认工具和配置(如 ESLint 配置)。如果你计划开源你的模块,保持这些默认配置能确保你的模块与其他社区模块拥有一致的编码规范,方便他人贡献代码。