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
}
}
})
这样,我们就可以在应用程序中的任何位置使用$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)
}
这样我们就可以随时随地记录这一事件:
// 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...
})
您可以在应用程序的任何位置为同一事件注册多个监听器。请注意,在某些情况下,监听器可以而且必须被移除。这里只是为了尽可能简化示例,以便您参考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
}
}
})
瞧!我们的编辑已经了解这一点,并将据此提出建议:
不仅如此,有效载荷还进行了类型化处理,因此如果我们提供的类型不正确,我们的应用程序会发出警告:
现在我们可以使用事件总线来触发任何我们想要的事件,并捕获它们进行处理。
根据 @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
// fire an event
useEvent('user:registered', { name: 'Israel'})
// capture
useListen('user:registered', (user) => console.log(user))
这样我们就可以受益于 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


