---
title: "使用钩子 & 扩展类型"
description: "掌握模块中的生命周期钩子、虚拟文件和 TypeScript 声明。"
canonical_url: "https://nuxt.zhcndoc.com/docs/3.x/guide/modules/recipes-advanced"
---
# 使用钩子 & 扩展类型

> 掌握模块中的生命周期钩子、虚拟文件和 TypeScript 声明。

这里介绍一些编写模块的高级模式，包括钩子、模板和类型增强。

## 使用生命周期钩子

[生命周期钩子](https://nuxt.zhcndoc.com/docs/3.x/guide/going-further/hooks) 可以让你扩展 Nuxt 的几乎所有方面。模块可以通过编程方式或在定义中的 `hooks` 映射表中绑定钩子。

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

export default defineNuxtModule({
  // 通过 `hooks` 映射表绑定 `app:error` 钩子
  hooks: {
    'app:error': (err) => {
      console.info(`发生了这个错误: ${err}`)
    },
  },
  setup (options, nuxt) {
    // 程序化绑定 `pages:extend` 钩子
    nuxt.hook('pages:extend', (pages) => {
      console.info(`发现了 ${pages.length} 个页面`)
    })
  },
})
```

::read-more{to="https://nuxt.zhcndoc.com/docs/3.x/api/advanced/hooks"}
::

::tip{icon="i-lucide-video" to="https://vueschool.io/lessons/nuxt-lifecycle-hooks?friend=nuxt" target="_blank"}
观看 Vue School 关于在模块中使用 Nuxt 生命周期钩子的视频。
::

::note
**模块清理**
:br
:br
如果你的模块打开了监听器或其他资源，或启动了 watcher，应该在 Nuxt 生命周期结束时关闭它们。可以使用 `close` 钩子来完成这项工作。

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

export default defineNuxtModule({
  setup (options, nuxt) {
    nuxt.hook('close', async (nuxt) => {
      // 在此处编写自定义关闭代码
    })
  },
})
```
::

### 创建自定义钩子

模块也可以定义并调用自己的钩子，这是使模块可扩展的强大范式。

如果你希望其他模块能够订阅你模块的钩子，应该在 `modules:done` 钩子中调用它们。这样可以确保所有其他模块都有机会完成设置并在它们自己的 `setup` 函数中注册钩子监听。

```ts
// my-module/module.ts
import { defineNuxtModule } from '@nuxt/kit'

export interface ModuleHooks {
  'my-module:custom-hook': (payload: { foo: string }) => void
}

export default defineNuxtModule({
  setup (options, nuxt) {
    // 在 `modules:done` 事件中调用自定义钩子
    nuxt.hook('modules:done', async () => {
      const payload = { foo: 'bar' }
      await nuxt.callHook('my-module:custom-hook', payload)
    })
  },
})
```

## 添加虚拟文件

如果你需要添加可在用户应用中导入的虚拟文件，可以使用 `addTemplate` 工具。

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

export default defineNuxtModule({
  setup (options, nuxt) {
    // 文件会加入 Nuxt 内部的虚拟文件系统，可通过 '#build/my-module-feature.mjs' 导入
    addTemplate({
      filename: 'my-module-feature.mjs',
      getContents: () => 'export const myModuleFeature = () => "hello world !"',
    })
  },
})
```

对于服务端，应该使用 `addServerTemplate` 工具。

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

export default defineNuxtModule({
  setup (options, nuxt) {
    // 文件会加入 Nitro 的虚拟文件系统，可在服务端代码中通过 'my-server-module.mjs' 导入
    addServerTemplate({
      filename: 'my-server-module.mjs',
      getContents: () => 'export const myServerModule = () => "hello world !"',
    })
  },
})
```

## 更新虚拟文件

如果你需要更新模板/虚拟文件，可以利用 `updateTemplates` 工具，如下所示：

```ts
nuxt.hook('builder:watch', (event, path) => {
  if (path.includes('my-module-feature.config')) {
    // 这会重新加载你注册的模板
    updateTemplates({ filter: t => t.filename === 'my-module-feature.mjs' })
  }
})
```

## 添加类型声明

你可能还希望为用户项目添加类型声明（例如增强 Nuxt 接口或提供你自己的全局类型）。Nuxt 提供了 `addTypeTemplate` 工具，用于将模板写入磁盘，并在生成的 `nuxt.d.ts` 文件中添加其引用。

如果你的模块需要增强 Nuxt 处理的类型，可以使用 `addTypeTemplate` 来进行此操作：

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

export default defineNuxtModule({
  setup (options, nuxt) {
    addTypeTemplate({
      filename: 'types/my-module.d.ts',
      getContents: () => `// 由 my-module 生成
        interface MyModuleNitroRules {
          myModule?: { foo: 'bar' }
        }
        declare module 'nitropack/types' {
          interface NitroRouteRules extends MyModuleNitroRules {}
          interface NitroRouteConfig extends MyModuleNitroRules {}
        }
        export {}`,
    })
  },
})
```

如果需要更细粒度的控制，可以使用 `prepare:types` 钩子注册回调，注入你的类型声明。

```ts
const template = addTemplate({ /* 模板选项 */ })
nuxt.hook('prepare:types', ({ references }) => {
  references.push({ path: template.dst })
})
```

## 扩展 TypeScript 配置

有多种方式通过你的模块扩展用户项目的 TypeScript 配置。

最简单的方法是直接修改 Nuxt 配置，如下示例：

<!-- @case-police-ignore tsConfig -->

```ts
// 扩展 tsconfig.app.json
nuxt.options.typescript.tsConfig.include ??= []
nuxt.options.typescript.tsConfig.include.push(resolve('./augments.d.ts'))

// 扩展 tsconfig.shared.json
nuxt.options.typescript.sharedTsConfig.include ??= []
nuxt.options.typescript.sharedTsConfig.include.push(resolve('./augments.d.ts'))

// 扩展 tsconfig.node.json
nuxt.options.typescript.nodeTsConfig.include ??= []
nuxt.options.typescript.nodeTsConfig.include.push(resolve('./augments.d.ts'))

// 扩展 tsconfig.server.json
nuxt.options.nitro.typescript ??= {}
nuxt.options.nitro.typescript.tsConfig ??= {}
nuxt.options.nitro.typescript.tsConfig.include ??= []
nuxt.options.nitro.typescript.tsConfig.include.push(resolve('./augments.d.ts'))
```

或者，你可以使用 `prepare:types` 和 `nitro:prepare:types` 钩子来扩展特定类型上下文的 TypeScript 引用，或者类似上述示例那样修改 TypeScript 配置。

```ts
nuxt.hook('prepare:types', ({ references, sharedReferences, nodeReferences }) => {
  // 扩展应用上下文
  references.push({ path: resolve('./augments.d.ts') })
  // 扩展共享上下文
  sharedReferences.push({ path: resolve('./augments.d.ts') })
  // 扩展 Node 上下文
  nodeReferences.push({ path: resolve('./augments.d.ts') })
})

nuxt.hook('nitro:prepare:types', ({ references }) => {
  // 扩展服务端上下文
  references.push({ path: resolve('./augments.d.ts') })
})
```

::note
TypeScript 引用会将文件添加到类型上下文中，[不会受 `tsconfig.json` 中 `exclude` 选项的影响](https://www.typescriptlang.org/tsconfig/#exclude)。
::

## 增强类型

Nuxt 会自动将你的模块目录包含到相应的类型上下文中。要增强模块的类型，只需将类型声明文件放到对应增强类型上下文的合适目录。或者，你也可以[扩展 TypeScript 配置](#%E6%89%A9%E5%B1%95-typescript-%E9%85%8D%E7%BD%AE)从任意位置增强类型。

- `my-module/runtime/` - 应用类型上下文（不包括 `runtime/server` 目录）
- `my-module/runtime/server/` - 服务端类型上下文
- `my-module/` - Node 类型上下文（不包括 `runtime/` 和 `runtime/server` 目录）

```bash [目录结构]
-| my-module/   # Node 类型上下文
---| runtime/   # 应用类型上下文
------| augments.app.d.ts
------| server/ # 服务端类型上下文
---------| augments.server.d.ts
---| module.ts
---| augments.node.d.ts
```

### 已知限制

#### 在应用上下文中对服务端路由进行类型检查

除了使用 `tsconfig.server.json` 外，服务端路由还会使用 `tsconfig.app.json` 进行类型检查。

这是必需的，因为 Nuxt 会推断你的服务端端点的返回类型，以便在 [`$fetch`](https://nuxt.zhcndoc.com/docs/3.x/api/utils/dollarfetch) 和 [`useFetch`](https://nuxt.zhcndoc.com/docs/3.x/api/composables/use-fetch) 中提供响应类型。

::warning
这可能会导致在路由文件中使用 **仅限服务端的类型** 时出错。例如，如果一个模块使用 [`addServerTemplate`](https://nuxt.zhcndoc.com/docs/3.x/api/kit/templates#addservertemplate) 创建了仅服务端的虚拟文件，并且你在 `tsconfig.server.json` 中声明了相关类型，这些类型声明只在服务端上下文可用。当应用上下文对服务端路由进行类型检查时，无法识别这些仅限服务端的类型，会报错。为了解决此问题，你不得不在应用上下文中也声明这些类型。
::


## Sitemap

See the full [sitemap](https://nuxt.zhcndoc.com/sitemap.md) for all pages.
