使用钩子与扩展类型
在你的模块中掌握生命周期钩子、虚拟文件和 TypeScript 声明。
以下是一些编写模块的高级模式,包括钩子、模板和类型增强。
使用生命周期钩子
生命周期钩子 让你可以扩展 Nuxt 的几乎每个方面。模块可以通过编程方式钩入它们,或通过定义中的 hooks 映射来钩入。
import { addPlugin, createResolver, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
// 通过 `hooks` 映射钩入 `app:error` 钩子
hooks: {
'app:error': (err) => {
console.info(`This error happened: ${err}`)
},
},
setup (options, nuxt) {
// 通过编程方式钩入 `pages:extend` 钩子
nuxt.hook('pages:extend', (pages) => {
console.info(`Discovered ${pages.length} pages`)
})
},
})
如果你的模块打开、处理或启动了一个 watcher,你应该在 Nuxt 生命周期结束时关闭它。
close 钩子可用于此。import { defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
nuxt.hook('close', async (nuxt) => {
// 你的自定义代码放在这里
})
},
})
创建自定义钩子
模块也可以定义并调用自己的钩子,这是一种让你的模块可扩展的强大模式。
如果你希望其它模块能够订阅你模块的钩子,你应该在 modules:done 钩子中调用它们。这确保所有其它模块都有机会被设置,并在它们自己的 setup 函数中注册它们对你钩子的监听器。
// 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 工具函数。
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 工具函数。
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 工具函数:
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 来执行此操作:
import { addTemplate, addTypeTemplate, defineNuxtModule } 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 钩子注册一个回调,它将注入你的类型。
const template = addTemplate({ /* template options */ })
nuxt.hook('prepare:types', ({ references }) => {
references.push({ path: template.dst })
})
扩展 TypeScript 配置
有多种方式可以从你的模块扩展用户的 TypeScript 配置。
最简单的方式是直接修改 Nuxt 配置,像这样:
// 扩展 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.typescript.serverTsConfig.include ??= []
nuxt.options.typescript.serverTsConfig.include.push(resolve('./augments.d.ts'))
或者,你可以使用 prepare:types 和 nitro:prepare:types 钩子来为特定的类型上下文扩展 TypeScript 引用,或修改类似于上面示例的 TypeScript 配置。
nuxt.hook('prepare:types', ({ references, sharedReferences, nodeReferences }) => {
// 扩展 app 上下文
references.push({ path: resolve('./augments.d.ts') })
// 扩展 shared 上下文
sharedReferences.push({ path: resolve('./augments.d.ts') })
// 扩展 node 上下文
nodeReferences.push({ path: resolve('./augments.d.ts') })
})
nuxt.hook('nitro:prepare:types', ({ references }) => {
// 扩展 server 上下文
references.push({ path: resolve('./augments.d.ts') })
})
tsconfig.json 中 exclude 选项的影响。增强类型
Nuxt 会自动将你的模块目录包含在适当的类型上下文中。要从你的模块增强类型,你只需根据所增强的类型上下文将类型声明文件放在适当的目录中。或者,你可以扩展 TypeScript 配置 来从任意位置增强。
my-module/runtime/- app 类型上下文(除了runtime/server目录)my-module/runtime/server/- server 类型上下文my-module/- node 类型上下文(除了runtime/和runtime/server目录)
-| my-module/ # node 类型上下文
---| runtime/ # app 类型上下文
------| augments.app.d.ts
------| server/ # server 类型上下文
---------| augments.server.d.ts
---| module.ts
---| augments.node.d.ts
已知限制
在 App 上下文中对服务端路由进行类型检查
除了 tsconfig.server.json 之外,服务端路由也使用 tsconfig.app.json 进行类型检查。
这是必需的,因为 Nuxt 会推断你的服务端端点的返回类型,以在 $fetch 和 useFetch 中提供响应类型。
addServerTemplate 创建一个仅服务端的虚拟文件,并且你在 tsconfig.server.json 中为它声明了类型,那么这些类型声明将只在 server 上下文中可用。当 app 上下文对你的服务端路由进行类型检查时,它无法识别这些仅服务端的类型,并会报告错误。遗憾的是,要解决这个问题,你还需要在 app 上下文中声明这类类型。