发布于 2026-01-06 0 阅读
0

Nuxt 3 中的事件总线模式,并完全支持 TypeScript

Nuxt 3 中的事件总线模式,并完全支持 TypeScript

我需要在用户进行一些常见的交互时执行特定操作。这些交互可能相同,但位于应用程序的不同部分,因此需要可重用性。我发现,要以结构化和有序的方式通知用户这些交互的发生并做出响应,最简单的方法是使用事件总线模式。

使用事件总线可以轻松实现应用程序不同部分之间的通信。事件可以从应用程序的任何位置发出(可以包含或不包含有效负载),然后触发一些监听器函数(这些函数会接收有效负载作为参数),这些监听器函数可能已挂钩到该特定事件。就像 Vue 组件发出事件后,其父组件可以捕获该事件并执行相应的代码一样,事件总线本质上与之相同,但它的作用域更广,并且没有父子结构。

自 Vue 3 发布以来,使用 Vue 实例作为事件总线已不再是最佳方案。如果要在应用中实现这种模式,我们可能需要像vuejs.orgMitt推荐的那种库(虽然我在文档中找不到它了,但我很确定它曾经在那里)。

在本文中,我将向您展示如何使用Mitt在我的 Nuxt 3 应用程序中实现这种模式,Mitt 本身就提供了完整的 TypeScript 支持。为了更好地理解其底层原理,我建议您在继续阅读之前快速浏览一下Mitt 的文档

Mitt 将充当总线本身,因此我们只需要创建一个插件,就能以更“Nuxt 方式”使用它的功能。让我们创建一个插件(plugins/event-bus.ts),并将我们需要的 Mitt 功能映射到我们的应用程序中。

我假设您对 Nuxt 插件和辅助函数提供程序的工作原理有所了解。请访问“自动提供辅助函数”了解更多信息。

Mitt 的 API 和 Vue 事件的命名非常相似(`$_events`$emit/emit和 ` $on/on$_events`)。为了避免潜在的冲突和混淆,我已经将这些方法映射到Laravel 的 `$_events`event和`$_events` 类。listen



import mitt from 'mitt'

export default defineNuxtPlugin(() => {
  const emitter = mitt()

  return {
    provide: {
      event: emitter.emit, // Will emit an event
      listen: emitter.on // Will register a listener for an event
    }
  }
})


Enter fullscreen mode Exit fullscreen mode

这样,我们就可以在应用程序中的任何位置使用$event函数$listen,只需使用useNuxtApp可组合对象即可:



// components/register.vue
const { $event } = useNuxtApp()

const onUserRegistered = (user) => {
  // User registration form was saved and then we want
  // to notify the entire application about that.
  $event('user:registered', user)
}


Enter fullscreen mode Exit fullscreen mode

这样我们就可以随时随地记录这一事件:



// app.vue 
// ideally you will have a plugin (or multiple) to register listeners

const { $event } = useNuxtApp()

$listen('user:registered', (user) => {
  console.log('A user was registered!', user)
  // do something here...
  // An interesting example for this use case could be to 
  // just register an event or a conversion in your 
  // favourite analytics tool like Google Analytics, Fathom...
})


Enter fullscreen mode Exit fullscreen mode

您可以在应用程序的任何位置为同一事件注册多个监听器。请注意,在某些情况下,监听器可以而且必须被移除。这里只是为了尽可能简化示例,以便您参考Mitt 文档获取更多信息。

以上就是事件总线的工作原理!但现在我们想更进一步,由于 Mitt 支持 TypeScript,我们可以确保触发的事件名称和安全的有效负载都是安全的。同样基于Mitt 文档,我们可以像这样改进我们的插件:



import mitt from 'mitt'

interface User {
  name: string
}

type ApplicationEvents = {
  // translates to: 
  // user:registered -> event name
  // User -> type as payload
  'user:registered': User
  'user:deleted': User
};

export default defineNuxtPlugin(() => {
  // Then we just inform mitt about our event types
  const emitter = mitt<ApplicationEvents>()

  return {
    provide: {
      event: emitter.emit, // Will emit an event
      listen: emitter.on // Will register a listener for an event
    }
  }
})


Enter fullscreen mode Exit fullscreen mode

瞧!我们的编辑已经了解这一点,并将据此提出建议:

VS Code 建议

不仅如此,有效载荷还进行了类型化处理,因此如果我们提供的类型不正确,我们的应用程序会发出警告:

触发事件截图

TypeScript 警告

现在我们可以使用事件总线来触发任何我们想要的事件,并捕获它们进行处理。

根据 @jacobandrewsk 的评论,我还会展示一个如何将其用作可组合组件而不是插件的示例:



// composables/useEventBus.ts
import mitt from 'mitt'

type ApplicationEvents = {
  'user:registered': User
};

const emitter = mitt<ApplicationEvents>()

export const useEvent = emitter.emit
export const useListen = emitter.on


Enter fullscreen mode Exit fullscreen mode


// fire an event
useEvent('user:registered', { name: 'Israel'})
// capture
useListen('user:registered', (user) => console.log(user))


Enter fullscreen mode Exit fullscreen mode

这样我们就可以受益于 Nuxt 3 的自动导入功能,并立即在应用程序中的任何位置使用这两个函数。

结论

这里我展示了一个非常基本的例子,但你的应用程序可以触发和响应的事件数量没有限制。

我发现这种模式对于代码复用和扩展应用程序的某些部分特别有用。例如,集成第三方服务(如上例所示)或显示通知。您可以尽情发挥想象力,看看它在其他方面是否也能发挥作用。

请在下方留言告诉我你的想法!👇

还有一件事

我是 VueJobs 的联合创始人——VueJobs 是排名第一的 Vue.js 招聘网站——如果您想招聘 Vue.js 开发人员寻找 Vue.js 相关工作,欢迎访问 VueJobs。

我的推特账号是:IsraelOrtuno

文章来源:https://dev.to/israelortuno/event-bus-pattern-in-nuxt-3-with-full-typescript-support-1okp