跳到主要内容

添加插件、组件等

学习如何从你的模块注入插件、组件、组合式函数和服务端路由。

以下是模块作者常用的一些模式。

修改 Nuxt 配置

Nuxt 配置可以被模块读取和修改。下面是一个模块启用某个实验性特性的示例。

import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    // 如果 `experimental` 对象尚不存在,我们就创建它
    nuxt.options.experimental ||= {}
    nuxt.options.experimental.componentIslands = true
  },
})

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

观看 Vue School 关于修改 Nuxt 配置的视频。

将选项暴露给运行时

因为模块不属于应用运行时,它们的选项也不例外。然而,在许多情况下,你可能需要在运行时代码中访问其中一些模块选项。我们建议使用 Nuxt 的 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 来扩展用户提供的公共运行时配置,而不是覆盖它。

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

import { useRuntimeConfig } from '@nuxt/kit'

const options = useRuntimeConfig().public.myModule
小心不要在公共运行时配置中暴露任何敏感的模块配置,例如私有 API 密钥,因为它们会被包含进公共包中。
观看 Vue School 关于传递和暴露 Nuxt 模块选项的视频。

添加插件

插件是模块添加运行时逻辑的常用方式。你可以使用 addPlugin 工具函数从你的模块注册它们。

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

export default defineNuxtModule({
  setup (options, nuxt) {
    // 创建 resolver 以解析相对路径
    const resolver = createResolver(import.meta.url)

    addPlugin(resolver.resolve('./runtime/plugin'))
  },
})

添加组件

如果你的模块应该提供 Vue 组件,你可以使用 addComponent 工具函数将它们作为自动导入添加,供 Nuxt 解析。

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

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

    // 来自运行时目录
    addComponent({
      name: 'MySuperComponent', // 在 vue 模板中使用的组件名
      export: 'MySuperComponent', // (可选)如果组件是命名(而非默认)导出
      filePath: resolver.resolve('runtime/app/components/MySuperComponent.vue'),
    })

    // 来自某个库
    addComponent({
      name: 'MyAwesomeComponent', // 在 vue 模板中使用的组件名
      export: 'MyAwesomeComponent', // (可选)如果组件是命名(而非默认)导出
      filePath: '@vue/awesome-components',
    })
  },
})

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

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

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

    addComponentsDir({
      path: resolver.resolve('runtime/app/components'),
    })
  },
})
强烈建议为你的导出添加前缀,以避免与用户代码或其它模块冲突。阅读更多
注意,所有组件、页面、组合式函数以及其它通常会放在你的 app/ 文件夹中的文件,都需要放在 runtime/app/ 中。这将意味着它们能被正确地进行类型检查。

添加组合式函数

如果你的模块应该提供组合式函数,你可以使用 addImports 工具函数将它们作为自动导入添加,供 Nuxt 解析。

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

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

    addImports({
      name: 'useComposable', // 要使用的组合式函数名
      as: 'useMyComposable', // (可选)消费应用可用的别名
      from: resolver.resolve('runtime/app/composables/useComposable'), // 组合式函数的路径
    })
  },
})

可以传入多个条目作为数组:

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

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

    addImports([
      { name: 'useFirstComposable', from: resolver.resolve('runtime/composables/useFirstComposable') },
      { name: 'useSecondComposable', from: resolver.resolve('runtime/composables/useSecondComposable') },
    ])
  },
})

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

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

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

    addImportsDir(resolver.resolve('runtime/composables'))
  },
})
强烈建议为你的导出添加前缀,以避免与用户代码或其它模块冲突。阅读更多
注意,所有组件、页面、组合式函数以及其它通常会放在你的 app/ 文件夹中的文件,都需要放在 runtime/app/ 中。这将意味着它们能被正确地进行类型检查。

添加带键函数

有时,你可能需要保持服务端和客户端之间的状态一致性。Nuxt 内置的 useStateuseAsyncData 组合式函数就是例子。Nuxt 提供了一种方式来注册这类函数,以实现自动键注入。

当一个函数被注册后,如果该函数以少于指定数量的参数被调用,Nuxt 的编译器会自动注入一个唯一的键作为额外参数。这个键在服务端渲染和客户端水合之间保持稳定。

注入的键是一个派生自文件路径和调用位置哈希值。

使用 keyedComposables 选项来注册你的函数:

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

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

    nuxt.options.optimization.keyedComposables.push({
      name: 'useMyState',
      source: resolver.resolve('./runtime/composables/state'),
      argumentLength: 2,
    })
  },
})

keyedComposables 配置接受一个对象数组,具有以下属性:

属性类型描述
namestring函数名。默认导出使用 'default'(可调用名称将从文件名派生为 camelCase)。
sourcestring函数定义文件的解析路径。支持 Nuxt 别名(~@ 等)
argumentLengthnumber函数接受的最大参数数量。当以更少参数调用时,会注入一个唯一的键。

例如,当 argumentLength: 2

useMyState() // useMyState('$HJiaryoL2y')
useMyState('myKey') // useMyState('myKey', '$HJiaryoL2y')
useMyState('a', 'b') // 不转换(已经有 2 个参数)
键注入插件会验证每个函数调用的精确解析导入源。它不会跟踪 barrel 导出。函数必须从 source 属性中指定的确切源文件导出。
函数调用必须是静态可分析的。编译器无法为动态或间接的函数调用注入键。

添加路由中间件

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

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

    // 添加全局路由中间件的示例
    addRouteMiddleware({
      global: true,
      name: 'name-of-your-middleware',
      path: resolver.resolve('./runtime/middleware/name-of-your-middleware'),
    })
  },
})

添加服务端路由

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

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

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

你也可以添加动态服务端路由:

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

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

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

    // 或者使用全捕获路由
    addServerHandler({
      route: '/api/_my-module/files/**:path',
      handler: resolver.resolve('./runtime/server/api/files/[...path].get'),
    })
  },
})
强烈建议为你的服务端路由添加前缀,以避免与用户定义的路由冲突。像 /api/auth/api/login/api/user 这样的常见路径可能已经被应用使用。阅读更多

添加其它资源

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

import { addPlugin, createResolver, defineNuxtModule } 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 选项暴露一个资源文件夹:

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

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

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

使用其它模块

如果你的模块依赖其它模块,你可以使用 moduleDependencies 选项指定它们:

import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  meta: {
    name: 'my-module',
  },
  moduleDependencies: {
    '@nuxtjs/tailwindcss': {},
  },
})
阅读更多