TL;DR — nuxt-socket-io is a module for the Nuxt framework that allows you to configure IO easily: that is, configure your socket.IO clients once, and then just use them. It plays nicely with Vuex so you can stream incoming data directly to your application state. This article explains how to use it.
Disclaimer: I am the author of the nuxt-socket-io module.
Prerequisites: (listed in order)
- VueJS: The progressive JS framework. For the impatient: focus on the components and computed properties. Ignore the installation guide because installing Nuxt (next bullet) will install VueJS.
- Nuxt: The progressive VueJS framework that makes it easy to organize and maintain your VueJS app. For the impatient: install npx with
npm i -g npx. Then create your Nuxt app with
npx create-nuxt-appand follow the prompts. It may be helpful to read the Modules Guide, although not completely required.
- Socket.IO: The real-time IO engine for the web
- Vuex: [Global] State management for your app. For understanding core concepts, go here. For practical purposes though, it’s better to understand how Nuxt interacts with Vuex to help make development easier. I.e., configure your stores the “Nuxt way” if using Nuxt (see next bullet).
- Nuxt Vuex Configuration: Stores are managed in the “stores” folder. Pretty straightforward once you get the hang of it.
- “Rethinking Web IO”: Explains the motivation behind this module’s development
At a very high-level view,
nuxt-socket-iois a Nuxt module that wraps around the popular
socket.io-client. While it may be tempting to want to use that client directly, you may find yourself having to set up configurations in multiple locations and performing other common tasks repeatedly. When possible, developers want to strive for efficiency and maintainability: code less, repeat less, and accomplish more faster. The
nuxt-socket-io module lets you configure almost everything in the
nuxt.config.js file, yet still provides you with the
socket.io-client instance, so all the API methods you may already be familiar with can be used. Easier for you, easier for me to maintain the plugin.
The advantages of using this module are:
- Configure multiple IO sockets with meaningful names directly in the nuxt.config.js file. Easily specify a default, otherwise the first socket in the list will be default. When designing components, you may be interested to see what the component looks like with data injected into it. Various options are out there, but an easy one might be to configure a “test” socket that injects dummy data into your component. This may be particularly useful when the injected data size is large.
- Configure Vuex options directly in
- When certain events are received by your application, you may want to simply trigger Vuex mutations or actions. I.e., as data comes in, it gets written to the application state. When components need that data, simply read from the Vuex stores.
- When Vuex state changes, you may want changes of certain state properties to trigger notifications back to IO server(s). After all, who says reactivity has to be limited to the browser?
- It works well with what you have already configured in your stores folder.
npm i -S nuxt-socket-io
Registering the module:
nuxt.config.js simply add it to the modules list:
$nuxtSocket will be injected into the
context so you can easily use it in your app. However, in order to use it, you must also configure the module options:
The following is required for each socket:
- url: The client needs an IO server it can connect to
The following are recommended:
- default: Specifies the default socket. If you accidentally set this Boolean for multiple sockets, the first found default will be used.
- name: The name that will be used to look up the requested socket. If a name is not provided, the socket with default set to “true”. If neither a name or the default are set, the first socket will be used.
- vuex: The mapping of IO events to vuex mutations / actions / emit backs.
For the above example, the following will happen:
- mutations: When the IO client receives the event “progress”, it will trigger the Vuex “examples/SET_PROGRESS” mutation and pass the event’s data to the mutation. (If, instead, the option were just the string “examples/SET_PROGRESS”, then when that event is received, it would trigger the mutation)
- actions: When the IO client receives the event “chatMessage” it will trigger the Vuex action “‘FORMAT_MESSAGE”. Again, if just the string “FORMAT_MESSAGE” were specified, that would be treated as both the event name and the Vuex action.
- emitBacks: When the Vuex property “examples/sample” changes, it will emit back the “examples/sample” event with that property’s data. When the Vuex property “examples/sample2” changes, it will emit the mapped event back to the IO server with that property’s data. The
nuxt-socket-iomodule provides a utility to help mapping this state data so your components can easily use it. See below.
Following along with this example, the store is configured the “Nuxt way” in the “stores” folder. So,
stores/index.js contains the “root store”, and
stores/examples.js contains “examples store”.
OK, all the boring, boiler-plate work is done! But the idea with module is you should only have to do it once; maintenance should be straightforward. Now using the module in your Vue components is a breeze. Here are some examples:
The index page uses the
mapState helper to bind
this.chatMessages to the
chatMessages stored in Vuex. In order for
chatMessages to be updated in store though, we need to instantiate a socket.IO client. We do this in the
mounted() lifecycle hook. Notice, I don’t specify what name to use. By omission, the default socket will be used, which has the
vuex options set. I could have just as well specifed
name: home in the
$nuxtSocket to obtain the same instance. The
$nuxtSocket function also accepts a channel as an option, and all other options will be passed as connection options to the underlying
reconnection: false will be honored.
While the convenience of Vuex may be nice, you may not always want your data to live globally; you want it only in the scope of your component. In that case, the
$nuxtSocket instance is a
socket.io-client, so you can use it directly as you would expect: using
.emit methods and receiving the data in the callback. That’s exactly what is being done in the
Recall from the vuex options, we specified “emitBacks” in addition to mutations. Since vuex’s
mapState is “getter by default”, if it’s desired to have two way binding, a setter must be provided. There are two ways to accomplish the same thing.
- The module provides a “mapState2Way” util that can be imported from either
nuxt-socket-io/utils/esm. The former is in “CommonJS” format, the other is in “ESM” format. To setup a two way binding between the computed property “sample”, the
mapState2Waymethod needs a mapping of the Vuex property to the mutation that will perform the updating. So here, when the Vuex “examples/sample” property changes, we want to commit the “examples/SET_SAMPLE” mutation.
- This is the more manual approach, but gives an idea of what the utility is doing. When we want to bind the component’s sample2 property to the Vuex sample2 property, the getter returns it directly. When we want to update the property, we commit the “examples/SET_SAMPLE2” mutation.
Learn by Doing
- Clone the git repo: https://github.com/richardeschloss/nuxt-socket-io
- Build the dev server:
npm run dev:server
- Play with the app at: http://localhost:3000
As demonstrated above, the Nuxt-socket-io module allows socket.io-client to easily be integrated and configured in your Nuxt applications. It makes it easy for IO events to be mapped directly to your application state, but also exposes the socket.IO client API directly should you wish to use it. Hopefully, this article and the actual module proves to be helpful.
Credits / Acknowledgements
- Sebastian Lubke: for opening the issue asking for the Vuex option support.