npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vpubsub

v1.0.2

Published

Easy to use Pub/Sub broker, with built-in support for Vue 3.

Downloads

9

Readme

VPubSub

Easy-to-use Pub/Sub Broker (With built-in Vue 3.x plugin support)

What is It?

VPubSub implements the Publish/Subscribe pattern allowing you to subscribe to messages of interest, and publish those same messages from elsewhere within your application - your subscribers then receive these messages (via callback functions).

If you're not familiar with this pattern, it's a very flexible form of event handling. It allows communications between different components or services while keeping them decoupled - the publisher of an event never needs to know the identity of any potential subscribers, and vice-versa.

Keeping components of your application from being tightly coupled is generally good design, but you'll need to consider whether it's appropriate and helpful in your particular use-case - no tool is always the right tool, not every problem is a nail in want of a hammer. Pub/sub can become a problem in a complex application where events are coming in hot-and-heavy, because it can be difficult to track down just where any given event is coming from.

You should be careful in making events easily referenceable (e.g. don't use a bare string as your key, have a central place where all event names are visible and importable from), and opt for single-hops as often as possible (e.g. avoid having an event publication trigger a subscriber to publish another event which causes a subscriber to publish another event, etc. - instead, have a single publication trigger subscribers, without allowing the subscribers to publish their own events in turn).

If you know pub/sub is what you need, or you're considering it, read on!

How Does It Work?

Let's look at some common and uncommon use-cases, both in general, and then including Vue - both with the Options API, and the Composition API:

Subscribe to a single event

import {vent} from "vpubsub";

const EVENTS = {
    PLAYER:{
        START:'start.player',
        END:'end.player',
    }
};

vent.on(EVENTS.PLAYER.START, (arg1, arg2, ...rst) => {
    // Do something useful with the event and arguments.
    // The arguments will be whatever was passed during the call to `trigger`.
});

// ... elsewhere in your application ...
vent.trigger(EVENTS.PLAYER.START, 'event', 'args', 40, 'or', 'as', 'many as you like',)

Subscribe to all events on a channel

Did you notice the event.channel structure of the event name in our example above? All events within VPubSub belong to a channel - a way of organizing events together. This also allows us to perform operations on a channel-basis, like subscribe to all events for that channel.

import {vent} from "vpubsub";

const EVENTS = {
    PLAYER:{
        START:'start.player',
        END:'end.player',
    }
};

/**
 * Subscribers to wildcard events will receive the channelEvent as the last argument,
 * allowing for disambiguation within the handler.
 */
vent.on('*.player', (arg1, arg2, ..., channelEvent) => {
    switch(channelEvent){
        case 'start.player':
            break;
        case 'end.player':
            break;
        default:
            break;
    }
});

Subscribe to all events

While not recommended, you can add a subscriber to all events by subscribing to the wildcard * event on the wildcard * channel. This could be used for debugging events, as you'll be able to see all traffic across the bus:

/**
 * As with wildcard channel events, the channelEvent will always be the last argument
 * when subscribing to all events.
 */
vent.on('*.*', (arg1, arg2, ..., channelEvent) => {
    console.log(arg1, channelEvent);
});

Requests

In addition to one-way event publishing, we can also perform two-way event messaging by making requests. Requests always return a promise that resolves with the responses from all subscribers.

import {vent} from "vpubsub";

const EVENTS = {
    PLAYER:{
        START:'start.player',
        END:'end.player',
    }
};

vent.on(EVENTS.PLAYER.START, () => {
    // Perform some work and return a response.
    const data = {...};
    return data;
});

// ... elsewhere in your application ...

vent.request(EVENTS.PLAYER.START, async(res) => {
    // Do something useful with the response(s) from the subscriber(s).
    const [resp1] = await res;
    console.log(resp1);
});

The same special wildcard channel event and global wildcard that you can use with trigger also apply to request, as detailed above.

How Does It Work With Vue?

VPubSub comes with built-in support for using it as a plugin with Vue 3. First, let's install it, and then we'll take a look at using it with the Options or Composition APIs.

Installation

Let's create a plugins/vent.js file, that looks like this:

/**
 * Return our vent utility as a plugin in Vue for install.
 */

import {install} from "vpubsub";

export default install;

Then, in main.js (your Vue app's point of entry), we can use it like so:

import {createApp} from 'vue';
import vent from "plugins/vent";
import App from "App.vue";

const vue = createApp(App);

vue.use(vent);

You can also skip creating a plugin wrapper, and use it like so:

import {install as vent} from "vpubsub";
import {createApp} from 'vue';
import App from "App.vue";

const vue = createApp(App);

vue.use(vent);

Options API

When installed as a Vue plugin, VPubSub adds a new element to the Options API, the vent option. This should be an object similar to methods or computed in your component, that has the events as the key and either a direct function as the subscriber, or the name of a method in your component as a string.

For example:

<template>
	<div><!-- ...Your template contents... --></div>
</template>
<script lang="js">
const EVENTS = {
	PLAYER:{
		START:'start.player',
		END:'end.player',
	}
};

export default {
    name: "VComponent",
    props:{
    	vprop:{
    		type:Boolean,
    		default:true,
    	}
        // ... Your Props ...
    },
    vent:{
    	// Indirect binding - the named function within this component will be bound.
        [EVENTS.PLAYER.START]:'recalcPoints',
        // Direct binding - this function will be bound.
        [EVENTS.PLAYER.END](arg1, arg2){
        	// Called whenever the `EVENTS.PLAYER.END` event is triggered or requested.
        }
    },
    methods:{
    	/**
    	 * This method will be called whenever the `EVENTS.PLAYER.START` event is
    	 * triggered or requested.
    	 */
    	recalcPoints(arg1, arg2){
    		// Because the `vent` events are bound to the component, you can use
    		// this to get props, data, computed, etc. of the component.
    		console.log(this.vprop);
    	}
    },
};
</script>

All events specified in the vent object are bound to the context of the component on mounted, and unbound during beforeUnmount. This means that you can use this to refer to the context of the component within these functions and access data, props, computed properties, etc.; and that the listeners will be safely bound and unbound automagically for you without any need to manually subscribe or unsubscribe.

Composition API

Using VPubSub with the Composition API is similar to using it outside of Vue, and can be used in a setup script tag in single-file components.

<script setup>
    import {vent} from "vpubsub";
    import EVENTS from "events";
    
    function onStart(arg1, arg2){
        // Do something on player start.
    }
    function onStartRequest(arg1, arg2){
        // Return something player start request.
        return 42;
    }
    
    vent.on(EVENTS.PLAYER.START, onStart);
    vent.on(EVENTS.PLAYER.START, onStartRequest);
    
    vent.trigger(EVENTS.PLAYER.START, 1, 2);
    vent.request(EVENTS.PLAYER.START, 1, 2).then(async(res) => {
        const [res1, res2] = await res;
        console.log(res1, res2);
    });
</script>

What are my import options?

As for imports, you can choose between importing from vpubsub, which expects lodash-es as a peer dependency but doesn't bake in the parts it needs; or you can import from vpubsub.full which bakes in just those bits of lodash-es that it relies on.

This is pretty neat! I'd like to use it in my project.

Go for it. 👍

The License.txt file contains the details - this project is licensed under the Mozilla Public License, Version 2.0.