首页 / Nuxt 4 入门教程 / 插件 plugins

Nuxt 4 入门教程

插件 plugins

本教程共 50 篇 · 第 37 篇 · 更新于 2026-08-08 · 约 7 分钟阅读

NuxtNuxt4plugins插件provide全局能力

本节目标:理解 Nuxt 插件的运行时机,能用 defineNuxtPlugin 在应用启动时注入全局能力,并区分客户端/服务端插件。

37-1

组合式函数适合在「某个组件里」复用逻辑,但有些东西需要在「整个应用启动时」就准备好,比如:接入一个第三方库(分析工具、地图 SDK)、给所有请求加鉴权头、注册一个全局错误处理函数。这些东西不适合放在某个页面,而应该放在插件里。

插件的本质:一个在 Nuxt 应用初始化阶段运行的函数。它运行得比页面渲染更早,因此可以提前把全局能力「挂」到应用上,供后面所有组件使用。

37-2

插件文件放在 app/plugins/ 目录。文件名随意,但约定用清晰的小写:

export default defineNuxtPlugin((nuxtApp) => {
  // 插件运行时,你可以拿到 nuxtApp 实例
  console.log('应用启动了,我是 hello 插件')
})

只要把文件丢进 app/plugins/,Nuxt 就会自动加载它,不需要在 nuxt.config 里登记。开发服务器启动后,你会在控制台看到这条日志。

37-3

插件最常见的用途是「提供一个全局可用的函数」。比如我们想全站都能调用一个日期格式化工具:

export default defineNuxtPlugin((nuxtApp) => {
  return {
    provide: {
      formatDate: (input: string | Date) => {
        const d = new Date(input)
        return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`
      },
    },
  }
})

通过 provide 返回的对象,Nuxt 会把 formatDate 挂到应用实例上。之后你在任何地方都能这样用:

<script setup lang="ts">
const { $formatDate } = useNuxtApp()
const text = $formatDate('2026-08-07')
</script>

在模板里,它还会被自动导入成 $formatDate

<template>
  <p>{{ $formatDate('2026-08-07') }}</p>
</template>
Note

provide 里的键名会自动加 $ 前缀。你也可以用 nuxtApp.provide('formatDate', fn) 这种写法,效果一样。模板里写 $formatDate,脚本里写 useNuxtApp().$formatDate

37-4

有些库只能在浏览器跑(依赖 window、DOM),有些只在服务端有意义。Nuxt 用文件后缀区分:

  • app/plugins/track.client.ts:只在浏览器执行。
  • app/plugins/logger.server.ts:只在服务端执行。
export default defineNuxtPlugin(() => {
  // 这里可以安全使用 window、document 等浏览器 API
  console.log('只在客户端加载的插件')
})

没有后缀的插件(如 hello.ts)两端都会执行。但要注意:两端都会跑的代码,里面对浏览器 API 的访问要包在 import.meta.client 之类的判断里。

37-5

当插件很多时,顺序很重要。默认按文件名字母序执行。想手动控制顺序,可以加 order 字段,数字越小越先跑:

export default defineNuxtPlugin({
  name: 'setup-client',
  order: 1,
  setup() {
    // 最先执行的客户端插件
  },
})
Warning

插件在「激活(hydration)阶段」运行。如果插件里做了昂贵计算或耗时初始化,会拖慢首屏。能用组合式函数或工具函数替代的,就别写成插件。插件只留给「全局、一次性的初始化」。

37-6

默认插件是同步执行的。如果你的插件需要 await(比如从远程拉取配置),记得开启 parallel,否则它会阻塞后续插件:

export default defineNuxtPlugin(async (nuxtApp) => {
  const config = await $fetch('/api/client-config')
  return {
    provide: {
      appConfig: config,
    },
  }
})

如果你的 Nuxt 版本支持,给异步插件加 parallel: true 可以让多个异步插件并发执行,缩短启动时间。

37-7

插件是注册全局错误处理器的好地方。比如把应用里的 Vue 错误上报到监控服务:

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.config.errorHandler = (error, instance, info) => {
    // 这里可以上报错误,例如发送到 Sentry
    console.error('捕获到全局错误', error, info)
  }

  nuxtApp.hook('vue:error', (error) => {
    console.error('vue:error', error)
  })
})
Tip

vue:error 钩子基于 Vue 的 onErrorCaptured,能捕获所有传播到顶层的 Vue 错误,非常适合接错误监控。下一章讲模块时你会看到,这类「全局初始化」正是插件的典型场景。

37-8

Nuxt 3 的插件目录在项目根 plugins/,Nuxt 4 改到 app/plugins/defineNuxtPluginprovide.client/.server 后缀等用法完全一致。

37-9

前面几个例子都比较小。真实项目里,插件常用来「读运行时配置 + 注入一个全局 API 客户端」,把重复的请求前缀、鉴权头统一收口。

export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig()
  const api = $fetch.create({
    baseURL: config.public.apiBase,
    onRequest({ options }) {
      options.headers = options.headers || {}
      // 统一带上 token
    },
  })
  return {
    provide: {
      api,
    },
  }
})

之后任意组件都能 const { $api } = useNuxtApp(),然后 $api('/me') 发请求,不用每次写 baseURL。注意 useRuntimeConfig() 要在插件函数体内调用(仍在 Nuxt 上下文里),别写到文件顶层。

Note

provide 出去的东西在 SSR 和客户端都能用,但插件里拿到的 config.public.* 才会被打包进客户端;config.*(不带 public)只在服务端可见。把不该暴露的密钥放进 runtimeConfig 而非 runtimeConfig.public
调试插件时有个小技巧:因为插件在应用最早期运行,浏览器控制台最早打印的日志往往来自插件。如果某个 provide 的名字在组件里取不到,先确认插件确实跑了(加一行 console.log),再看 provide 的键名有没有加 $。另一个常见困惑:.client 插件里注入的东西,在纯服务端渲染阶段(生成 SSR 的 HTML 时)还不存在,所以别在服务端代码里依赖只在客户端才 provide 的能力。

Tip

不确定插件两端都跑还是只跑一端?在插件里写 console.log(import.meta.client ? 'client' : 'server'),开发时看控制台就知道当前跑的是哪一端,排查「为什么这个 provide 取不到」特别快。

37-10

插件是「应用级的一次性初始化」,放在 app/plugins/,用 defineNuxtPlugin 定义,用 provide 挂全局能力。靠 .client/.server 后缀区分运行环境,靠 order 控制顺序。别把能放进组合式函数的逻辑塞进插件。下一章我们看更「重量级」的扩展方式:模块。

37-7 插件的加载顺序与依赖

当项目有多个插件时,加载顺序可能很重要。默认情况下,Nuxt 按文件名的字母顺序加载插件。如果需要控制顺序,可以在文件名前加数字前缀,如 01.first.ts02.second.ts。数字小的先加载。

有些插件依赖其他插件先初始化。比如一个分析插件可能需要路由插件先注册好导航钩子。通过文件名排序可以确保这种依赖关系被满足。不过更好的做法是在插件内部做防御性检查,而不是过度依赖加载顺序。

插件的错误处理也值得注意。如果插件初始化时抛出错误,整个应用可能无法启动。建议在插件内部用 try-catch 包裹可能失败的逻辑,并提供降级方案。特别是涉及外部服务(如第三方 SDK)的插件,网络不通时不应该阻塞应用启动。

37-8 插件的类型扩展

Nuxt 插件不仅可以注入运行时代码,还能扩展 TypeScript 类型。当你的插件往 Nuxt 实例上添加了新的属性或方法时,应该同时提供类型声明,让使用者(包括你自己)在代码里获得正确的类型提示。

类型扩展通常通过声明合并(Declaration Merging)实现。在插件文件的同级目录创建一个 .d.ts 文件,用 declare module 扩展 Nuxt 的类型定义。这样 TypeScript 就能识别你插件注入的新功能。

良好的类型扩展是高质量插件的标志之一。它让使用者不需要翻阅文档,靠编辑器的类型提示就能知道插件提供了哪些功能、每个参数的类型是什么。