Socket.io bindings for Vue.js and Vuex

Socket.io bindings for Vue.js and Vuex

Vue-Socket.io-Extended Socket.io bindings for Vue.js and Vuex (inspired by Vue-Socket.io)

Vue-Socket.io-Extended

Socket.io bindings for Vue.js and Vuex (inspired by Vue-Socket.io)

Edit Vue Socket IO Extended Twitter feed demo

Features

  • Lightweight and dependency free - only 2kb min gzip
  • Reactive properties $socket.connected and $socket.disconnected
  • Listening and emitting socket.io events inside components
  • Auto-dispatches actions and mutations in multiple namespaced Vuex modules on socket.io events
  • Good TypeScript support (decorator and typing)
  • Can be used with any version of socket.io-client
  • Custom options - tweak the library to better fit your project needs
  • etc...

We support only browsers with global usage statistics greater then 1% and last 2 version of each browser (but not dead browsers). Library may work in older browser as well but we don't not guarantee that. You may need addition polyfills to make it work.

Motivation

I was using Vue-Socket.io for few months. I've liked the idea, but the more I used it the more I faced with bugs, outdated documentation, lack of support, absence of tests and a huge amount of issues . That slowed down development of the product I was working on. So I ended up with a decision to create my own fork with all the desirable stuff (features/fixes/tests/support/CI checks etc). That's how vue-socket.io-extended was born.

If you'd like to help - create an issue or PR. I will be glad to see any contribution. Let's make the world a better place

Prerequisites

You must have a running Socket.IO server before starting any Vue/Socket.IO project! Instructions on how to build a Node/Socket.IO server are found here.

Software Requirements

Installation

npm install vue-socket.io-extended socket.io-client

Initialization

import VueSocketIOExt from 'vue-socket.io-extended';
import io from 'socket.io-client';

const socket = io('http://socketserver.com:1923');

Vue.use(VueSocketIOExt, socket);

Note: you have to pass instance of socket.io-client as second argument to prevent library duplication. Read more here.

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/socket.io-client/dist/socket.io.slim.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-socket.io-extended"></script>
<script>
  var socket = io('http://socketserver.com:1923');
  Vue.use(VueSocketIOExt, socket);
</script>

Usage

On Vue.js component

Define your listeners under sockets section and they will listen corresponding socket.io events automatically.

new Vue({
  sockets: {
    connect() {
      console.log('socket connected')
    },
    customEmit(val) {
      console.log('this method was fired by the socket server. eg: io.emit("customEmit", data)')
    }
  },
  methods: {
    clickButton(val) {
      // this.$socket.client is `socket.io-client` instance
      this.$socket.client.emit('emit_method', val);
    }
  }
})

Note: Don't use arrow functions for methods or listeners if you are going to emit socket.io events inside. You will end up with using incorrect this. More info about this here

Create a new listener

this.$socket.$subscribe('event_name', payload => {
  console.log(payload)
});

Remove existing listener

this.$socket.$unsubscribe('event_name');

$socket.connected and $socket.diconnected are reactive. That means you can use them in expressions

<template>
  <div>
    <span>{{ $socket.connected ? 'Connected' : 'Disconnected' }}</span>
  </div>
</template>

Or conditions

<template>
  <span 
    class="notification" 
    v-if="$socket.disconnected"
  >
    You are disconnected
  </span>
</template>

Or computed properties, methods and hooks. Treat them as computed properties that are available in all components

Vuex Store Integration

Setup

To set up Vuex integration just pass the store as the third argument. In a Vue CLI project, you might do this in the src/main.js file. Example:

import VueSocketIOExt from 'vue-socket.io-extended';
import io from 'socket.io-client';
import store from './store'

const socket = io('http://socketserver.com:1923');

Vue.use(VueSocketIOExt, socket, { store });
Receiving Events

Mutations and actions will be dispatched or committed automatically in the Vuex store when a socket event arrives. A mutation or action must follow the naming convention below to recognize and handle a socket event.

  • A mutation should start with SOCKET_ prefix and continue with an uppercase version of the event
  • An action should start with socket_ prefix and continue with camelcase version of the event
Server Event Mutation Action
chat message SOCKET_CHAT MESSAGE socket_chatMessage
chat_message SOCKET_CHAT_MESSAGE socket_chatMessage
chatMessage SOCKET_CHATMESSAGE socket_chatMessage
CHAT_MESSAGE SOCKET_CHAT_MESSAGE socket_chatMessage

Check the Configuration section if you'd like to use a custom transformation.

Check the Migration from VueSocketIO section if you want to keep actions names in UPPER_CASE.

// In this example we have a socket.io server that sends message ID when it arrives
// so to get entire body of the message we need to make AJAX call the server
import Vue from 'vue'
import Vuex from 'vuex'

// `MessagesAPI.downloadMessageById` is an async function (goes to backend through REST Api and fetches all message data)
import MessagesAPI from './api/message'

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    // we store messages as a dictionary for easier access and interaction
    // @see https://hackernoon.com/shape-your-redux-store-like-your-database-98faa4754fd5
    messages: {},
    messagesOrder: []
  },
  mutations: {
    NEW_MESSAGE(state, message) {
      state.messages[message.id] = message;
      state.messagesOrder.push(message.id);
    }
  },
  actions: {
    socket_userMessage ({ dispatch, commit }, messageId) { // <-- this action is triggered when `user_message` is emmited on the server
      return MessagesAPI.downloadMessageById(messageId).then((message) => {
       commit('NEW_MESSAGE', message);
      })
    }
  }
})
Emitting Events

Events can be sent to the Socket.IO server by calling this._vm.$socket.client.emit from a Vuex mutation or action. Mutation or action names are not subject to the same naming requirements as above. More then one argument can be included. All serializable data structures are supported, including Buffer.

  actions: {
    emitSocketEvent(data) {
      this._vm.$socket.client.emit('eventName', data);
      this._vm.$socket.client.emit('with-binary', 1, '2', { 3: '4', 5: new Buffer(6) });
    }
  }
Namespaced Vuex Modules

Namespaced modules are supported out-of-the-box. Any appropriately-named mutation or action should work regardless of whether it's in a module or in the main Vuex store.

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

const messages = {
  state: {
    messages: []
  },
  mutations: {
    SOCKET_CHAT_MESSAGE(state, message) {
      state.messages.push(message);
    }
  },
  actions: {
    socket_chatMessage() {
      console.log('this action will be called');
    }
  },
};

const notifications = {
  state: {
    notifications: []
  },
  mutations: {
    SOCKET_CHAT_MESSAGE(state, message) {
      state.notifications.push({ type: 'message', payload: message });
    }
  },
};

export default new Vuex.Store({
  modules: {
    messages,
    notifications,
  }
})

The above code will:

  • Commit the SOCKET_CHAT_MESSAGE mutation in the messages module
  • Commit the SOCKET_CHAT_MESSAGE mutation in the notification module
  • Dispatch the socket_chatMessage action in the messages module

ECMAScript / TypeScript decorator (added in v4)

Required: ECMAScript stage 1 decorators. If you use Babel, babel-plugin-transform-decorators-legacy is needed. If you use TypeScript, enable --experimentalDecorators flag.

It does not support the stage 2 decorators yet since mainstream transpilers still transpile to the old decorators.

We provide @Socket() decorator for users of class-style Vue components. By default, @Socket() decorator listens the same event as decorated method name but you can use custom name by passing a string inside decorator e.g. @Socket('custom_event').

Check the example below:

<!-- App.vue -->
<script>
import Vue from 'vue'
import Component from 'vue-class-component'
import { Socket } from 'vue-socket.io-extended'

@Component({})
export default class App extends Vue {
  @Socket() // --> listens to the event by method name, e.g. `connect` 
  connect () {
    console.log('connection established');
  }

  @Socket('tweet')  // --> listens to the event with given name, e.g. `tweet`
  onTweet (tweetInfo) {
    // do something with `tweetInfo`
  }
}
</script>

Usage with Nuxt.js

The key point here is to disable SSR for the plugin as it will crash otherwise. It's a well-know issue and we are going to fix it. Thanks @ll931217 for investigation.

1. Create plugin:

// ~/plugins/socket.io.js
import Vue from 'vue';
import io from 'socket.io-client';
import VueSocketIOExt from 'vue-socket.io-extended';

const socket = io('http://localhost:3000');

export default ({ store }) => {
  Vue.use(VueSocketIOExt, socket, { store });
}

2. Then register it:

// nuxt.config.js
module.exports = {
  //...,
  plugins: [
    //...,
    { 
      src: '~/plugins/socket.io.js',
      ssr: false,                    // <-- this line is required
    },
  ]
}

Usage with Quasar Framework

Register vue-socket.io-extended with a boot file and disable server side rendering

1. Create bootfile:

// ~/boot/socket.io.js
import io from 'socket.io-client';
import VueSocketIOExt from 'vue-socket.io-extended';

const socket = io('http://localhost:3000');

export default async ({ store, Vue }) => {
  Vue.use(VueSocketIOExt, socket, { store })
}

2. Then register it:

// quasar.conf.js
module.exports = function (ctx) {
  return {
  //...,
  boot: [
    //...,
    { 
      path: 'socket.io',
      server: false,
    },
  ]
}

Configuration

In addition to store instance, vue-socket.io-extended accepts other options. Here they are:

Option Type Default Description
store Object undefined Vuex store instance, enables vuex integration
actionPrefix String 'socket_' Prepend to event name while converting event to action. Empty string disables prefixing
mutationPrefix String 'SOCKET_' Prepend to event name while converting event to mutation. Empty string disables prefixing
eventToMutationTransformer Function string => string uppercase function Determines how event name converted to mutation
eventToActionTransformer Function string => string camelcase function Determines how event name converted to action

FYI: You can always access default plugin options if you need it (e.g. re-use default eventToActionTransformer function):

import VueSocketIOExt from 'vue-socket.io-extended';
VueSocketIOExt.defaults // -> { actionPrefix: '...', mutationPrefix: '...', ... }

Migration from VueSocketIO

For everyone who has migrated from old package VueSocketIO to this new one on existing project. You need to redefine 2 parameters so you will be able to use old store actions names like "SOCKET_EVENT_NAME".

import VueSocketIO from 'vue-socket.io-extended';
import io from 'socket.io-client';

const ioInstance = io('https://hostname/path', {
        reconnection: true,
        reconnectionDelay: 500,
        maxReconnectionAttempts: Infinity
});
Vue.use(VueSocketIO, ioInstance, {
        store, // vuex store instance
        actionPrefix: 'SOCKET_', // keep prefix in uppercase
        eventToActionTransformer: (actionName) => actionName // cancel camel case
    }
});

FAQ

Semantic Versioning Policy

This plugin follows semantic versioning.

Changelog

We're using GitHub Releases.

Contribution

We're more than happy to see potential contributions, so don't hesitate. If you have any suggestions, ideas or problems feel free to add new issue, but first please make sure your question does not repeat previous ones.

Download Details:

Author: probil

Live Demo: https://codesandbox.io/s/oxqpm54pnq?fontsize=14&module=%2Fsrc%2FApp.vue

GitHub: https://github.com/probil/vue-socket.io-extended

vuejs vue javascript vue-js

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Vue ShortKey plugin for Vue.js

Vue-ShortKey - The ultimate shortcut plugin to improve the UX .Vue-ShortKey - plugin for VueJS 2.x accepts shortcuts globaly and in a single listener.

A Vue.js wrapper component for Grid.js

A Vue wrapper component for Grid.js. Grid.js is a Free and open-source HTML table plugin written in TypeScript. It works with most JavaScript frameworks.

Vue.js image clipping Components using Vue-Rx

vuejs-clipper .Vue.js image clipping components using Vue-Rx. Add image clipping components to your Vue application in nothing flat. Touch devices supported and fully responsive.

Vue.js JWT Authentication with Vuex and Vue Router

Build a Vue.js JWT Authentication application using Vuex, Vue Router, VeeValidate - JWT authentication with Vue, Vuex, Vue Router that supports VeeValidate

Vue.js JWT Authentication with Vuex and Vue Router

Vue Vuex JWT Auth .Vue.js JWT Authentication with Vuex and Vue Router