plugins

Nuxt 拥有一个插件系统,可以在创建 Vue 应用时使用 Vue 插件及更多功能。

Nuxt 会自动读取 plugins/ 目录中的文件,并在创建 Vue 应用时加载它们。

¥Nuxt automatically reads the files in the plugins/ directory and loads them at the creation of the Vue application.

所有插件都已自动注册,你无需单独将它们添加到你的 nuxt.config 中。
你可以在文件名中使用 .server.client 后缀,以便仅在服务器端或客户端加载插件。

已注册插件

¥Registered Plugins

只有目录顶层的文件(或任何子目录中的索引文件)才会自动注册为插件。

¥Only files at the top level of the directory (or index files within any subdirectories) will be auto-registered as plugins.

Directory structure
-| plugins/
---| foo.ts      // scanned
---| bar/
-----| baz.ts    // not scanned
-----| foz.vue   // not scanned
-----| index.ts  // currently scanned but deprecated

只有 foo.tsbar/index.ts 会被注册。

¥Only foo.ts and bar/index.ts would be registered.

要在子目录中添加插件,你可以在 nuxt.config.ts 中使用 plugins 选项:

¥To add plugins in subdirectories, you can use the plugins option in nuxt.config.ts:

nuxt.config.ts
export default defineNuxtConfig({
  plugins: [
    '~/plugins/bar/baz',
    '~/plugins/bar/foz'
  ]
})

创建插件

¥Creating Plugins

传递给插件的唯一参数是 nuxtApp

¥The only argument passed to a plugin is nuxtApp.

plugins/hello.ts
export default defineNuxtPlugin(nuxtApp => {
  // Doing something with nuxtApp
})

对象语法插件

¥Object Syntax Plugins

对于更高级的用例,也可以使用对象语法定义插件。例如:

¥It is also possible to define a plugin using an object syntax, for more advanced use cases. For example:

plugins/hello.ts
export default defineNuxtPlugin({
  name: 'my-plugin',
  enforce: 'pre', // or 'post'
  async setup (nuxtApp) {
    // this is the equivalent of a normal functional plugin
  },
  hooks: {
    // You can directly register Nuxt app runtime hooks here
    'app:created'() {
      const nuxtApp = useNuxtApp()
      // do something in the hook
    }
  },
  env: {
    // Set this value to `false` if you don't want the plugin to run when rendering server-only or island components.
    islands: true
  }
})

如果你使用对象语法,属性会被静态分析以生成更优化的构建。因此,你不应在运行时定义它们。:br 例如,设置 enforce: import.meta.server ? 'pre' : 'post' 将使 Nuxt 将来为你的插件进行的任何优化失效。 Nuxt 在使用对象语法时会静态预加载所有钩子监听器,让你无需担心插件注册的顺序即可定义钩子。

注册顺序

¥Registration Order

你可以通过在文件名前添加 'alphabetical' 编号来控制插件的注册顺序。

¥You can control the order in which plugins are registered by prefixing with 'alphabetical' numbering to the file names.

Directory structure
plugins/
 | - 01.myPlugin.ts
 | - 02.myOtherPlugin.ts

在此示例中,02.myOtherPlugin.ts 将能够访问由 01.myPlugin.ts 注入的任何内容。

¥In this example, 02.myOtherPlugin.ts will be able to access anything that was injected by 01.myPlugin.ts.

当你的插件依赖于另一个插件时,这很有用。

¥This is useful in situations where you have a plugin that depends on another plugin.

如果你不熟悉 'alphabetical' 编号,请记住文件名按字符串排序,而不是按数值排序。例如,10.myPlugin.ts 会排在 2.myOtherPlugin.ts 之前。这就是为什么示例中在单个数字前加上 0 前缀的原因。

加载策略

¥Loading Strategy

并行插件

¥Parallel Plugins

默认情况下,Nuxt 按顺序加载插件。你可以将插件定义为 parallel,这样 Nuxt 就不会等到插件执行结束后才加载下一个插件。

¥By default, Nuxt loads plugins sequentially. You can define a plugin as parallel so Nuxt won't wait until the end of the plugin's execution before loading the next plugin.

plugins/my-plugin.ts
export default defineNuxtPlugin({
  name: 'my-plugin',
  parallel: true,
  async setup (nuxtApp) {
    // the next plugin will be executed immediately
  }
})

依赖插件

¥Plugins With Dependencies

如果一个插件需要等待另一个插件运行,你可以将该插件的名称添加到 dependsOn 数组中。

¥If a plugin needs to wait for another plugin before it runs, you can add the plugin's name to the dependsOn array.

plugins/depending-on-my-plugin.ts
export default defineNuxtPlugin({
  name: 'depends-on-my-plugin',
  dependsOn: ['my-plugin'],
  async setup (nuxtApp) {
    // this plugin will wait for the end of `my-plugin`'s execution before it runs
  }
})

使用可组合组件

¥Using Composables

你可以在 Nuxt 插件中使用 composablesutils

¥You can use composables as well as utils within Nuxt plugins:

plugins/hello.ts
export default defineNuxtPlugin((nuxtApp) => {
  const foo = useFoo()
})

但是,请记住存在一些限制和差异:

¥However, keep in mind there are some limitations and differences:

If a composable depends on another plugin registered later, it might not work.
插件按顺序调用,且优先于其他所有操作。你可以使用依赖于尚未调用的另一个插件的组合项。
If a composable depends on the Vue.js lifecycle, it won't work.
通常,Vue.js 可组合项绑定到当前组件实例,而插件仅绑定到 nuxtApp 实例。

提供帮助函数

¥Providing Helpers

如果你希望在 NuxtApp 实例上提供一个辅助函数,请将其从插件中以 provide 键返回。

¥If you would like to provide a helper on the NuxtApp instance, return it from the plugin under a provide key.

export default defineNuxtPlugin(() => {
  return {
    provide: {
      hello: (msg: string) => `Hello ${msg}!`
    }
  }
})

然后,你可以在组件中使用助手:

¥You can then use the helper in your components:

components/Hello.vue
<script setup lang="ts">
// alternatively, you can also use it here
const { $hello } = useNuxtApp()
</script>

<template>
  <div>
    {{ $hello('world') }}
  </div>
</template>
请注意,我们强烈建议使用 composables 而不是提供辅助程序,以避免污染全局命名空间并保持主 bundle 条目较小。
如果你的插件提供了 refcomputed,它将不会被解包到组件 <template> 中。:br 这是由于 Vue 处理非模板顶层引用的方式所致。你可以在 在 Vue 文档中 上阅读更多相关信息。¥If your plugin provides a ref or computed, it will not be unwrapped in a component <template>.
This is due to how Vue works with refs that aren't top-level to the template. You can read more about it in the Vue documentation.

Typing 插件

¥Typing Plugins

如果你从插件返回辅助函数,它们将被自动输入;你会发现它们被输入到 useNuxtApp() 的返回值中,并且存在于你的模板中。

¥If you return your helpers from the plugin, they will be typed automatically; you'll find them typed for the return of useNuxtApp() and within your templates.

如果你需要在其他插件中使用提供的辅助程序,你可以调用 useNuxtApp() 来获取类型版本。但一般来说,除非你确定插件的顺序,否则应避免这种情况。

对于高级用例,你可以像这样声明注入属性的类型:

¥For advanced use-cases, you can declare the type of injected properties like this:

index.d.ts
declare module '#app' {
  interface NuxtApp {
    $hello (msg: string): string
  }
}

declare module 'vue' {
  interface ComponentCustomProperties {
    $hello (msg: string): string
  }
}

export {}
如果你使用的是 WebStorm,则可能需要增强 @vue/runtime-core,直到 this issue 问题得到解决。

Vue 插件

¥Vue Plugins

如果你想使用 Vue 插件(例如 vue-gtag)来添加 Google Analytics(分析)标签,可以使用 Nuxt 插件来实现。

¥If you want to use Vue plugins, like vue-gtag to add Google Analytics tags, you can use a Nuxt plugin to do so.

首先,安装 Vue 插件依赖:

¥First, install the Vue plugin dependency:

npm install --save-dev vue-gtag-next

然后创建一个插件文件:

¥Then create a plugin file:

plugins/vue-gtag.client.ts
import VueGtag, { trackRouter } from 'vue-gtag-next'

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(VueGtag, {
    property: {
      id: 'GA_MEASUREMENT_ID'
    }
  })
  trackRouter(useRouter())
})

Vue 指令

¥Vue Directives

同样,你可以在插件中注册自定义 Vue 指令。

¥Similarly, you can register a custom Vue directive in a plugin.

plugins/my-directive.ts
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.directive('focus', {
    mounted (el) {
      el.focus()
    },
    getSSRProps (binding, vnode) {
      // you can provide SSR-specific props here
      return {}
    }
  })
})
如果你注册了一个 Vue 指令,则必须在客户端和服务器端都注册它,除非你只在渲染一侧使用它。如果该指令仅在客户端有意义,你可以随时将其移动到 ~/plugins/my-directive.client.ts,并在 ~/plugins/my-directive.server.ts 中为服务器提供 'stub' 指令。¥If you register a Vue directive, you must register it on both client and server side unless you are only using it when rendering one side. If the directive only makes sense from a client side, you can always move it to ~/plugins/my-directive.client.ts and provide a 'stub' directive for the server in ~/plugins/my-directive.server.ts.
Read more in Custom Directives on Vue Docs.