模块创建指南
Nuxt 的 configuration 和 hooks 系统使你能够自定义 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
yarn create nuxt -t module my-module
pnpm create nuxt -- -t module my-module
bun 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:
- 在你选择的 IDE 中打开
my-module
- 使用你常用的包管理器安装依赖
- 使用
npm run dev:prepare
准备本地文件以供开发 - 请阅读本文档,了解更多关于 Nuxt 模块的信息。
使用 Starter
¥Using the Starter
了解如何使用模块启动器执行基本任务。
¥Learn how to perform basic tasks with the module starter.
如何开发
¥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 test
或npm 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 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
)
- 运行 linter (
- 然后,如果你的测试套件运行良好,它将通过以下方式发布你的模块:
- 根据约定式提交更新模块版本并生成更新日志
- 将模块发布到 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:
- 支持
defaults
和meta.configKey
自动合并模块选项 - 类型提示和自动类型推断
- 添加垫片以实现基本的 Nuxt 2 兼容性
- 确保模块仅使用从
meta.name
或meta.configKey
计算的唯一键安装一次。 - 自动注册 Nuxt 钩子
- 根据模块元数据自动检查兼容性问题
- 公开
getOptions
和getMeta
以供 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:
- Vue 组件
- 可组合函数
- Nuxt 插件
对于 服务器引擎,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.
#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.
@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.
向运行时暴露选项
¥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
使用 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'))
}
})
使用 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')
})
}
})
使用 addImports
和 addImportsDir
注入可组合项
¥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'))
}
})
还有更高级的方法,通过 Nitro 的 publicAssets
选项公开资源文件夹:
¥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`);
})
}
})
如果你的模块打开、处理或启动了监视器,则应在 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 IntegrationCheck 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:- 创建一个 Nuxt 应用,用作
test/fixtures/*
中的 "fixture"。 - 在测试文件中使用此 Fixture 设置 Nuxt
- 使用
@nuxt/test-utils
中的实用程序与 Fixture 交互(例如,获取页面) - 执行与此 Fixture(例如 "HTML 包含……")相关的检查
- 重复
// 1. Create a Nuxt application to be used as a "fixture"
import MyModule from '../../../src/module'
export default defineNuxtConfig({
ssr: true,
modules: [
MyModule
]
})
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 () => { /* ... */ })
使用 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.始终为暴露的接口添加前缀
¥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:- 为什么要使用这个模块?
- 如何使用此模块?
- 此模块的作用是什么?
提供 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!模块类型
¥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.