Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

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

:v::zap: Socket.io bindings for Vue.js and Vuex (inspired by Vue-Socket.io)
https://github.com/probil/vue-socket.io-extended

adapter realtime socket-io vuejs vuejs2 vuex websockets

Last synced: 5 days ago
JSON representation

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

Awesome Lists containing this project

README

        

[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct-single.svg)](https://stand-with-ukraine.pp.ua)

---

Vue-Socket.io-Extended


Build Status
Version
Downloads
License
Vue.js 2.x compatible
Minified library size
Code coverage (codecov)
Join us Gitter


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


Edit Vue Socket IO Extended Twitter feed demo



Buy Me a Coffee at ko-fi.com

> :warning: [The alpha version of v5](https://github.com/probil/vue-socket.io-extended/tree/alpha) (with Vue 3 support) has been released. Your feedback would be appreciated [here](https://github.com/probil/vue-socket.io-extended/issues/489)

## :cherries: 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...

## :heavy_check_mark: Browser Support

|![Chrome](https://raw.github.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/src/safari/safari_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/src/opera/opera_48x48.png) | ![Edge](https://raw.github.com/alrra/browser-logos/master/src/edge/edge_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png) |
| --- | --- | --- | --- | --- | --- |
| 38+ :heavy_check_mark: | 13+ :heavy_check_mark: | 8+ :heavy_check_mark: | 25+ :heavy_check_mark: | 12+ :heavy_check_mark: | 11+ :heavy_check_mark: |

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

## :seedling: Motivation

I was using [`Vue-Socket.io`](https://github.com/MetinSeylan/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 :disappointed:. 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 :heart:

## :grey_exclamation: 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](https://socket.io).

## :grey_exclamation: Software Requirements

- [Vue.js](https://vuejs.org/) `>=2.X`
- [Socket.io-client](https://socket.io) `>=2.X`
- [Vuex](https://vuex.vuejs.org/) `>=2.X` (optional)

## :cd: Installation

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

## :checkered_flag: Initialization

#### ES2015 (Webpack/Rollup/Browserify/Parcel/etc)
```js
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](https://github.com/probil/vue-socket.io-extended/issues/19).

#### UMD (Browser)

```html

var socket = io('http://socketserver.com:1923');
Vue.use(VueSocketIOExt, socket);

```

## :rocket: Usage

#### On Vue.js component

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

```js
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](https://github.com/probil/vue-socket.io-extended/issues/61)

#### Dynamic socket event listeners (changed in v4)

There is a way to create listeners dynamically, in case you need to start listening only on some condition.
```js
// creating event listener
this.$socket.$subscribe('event_name', payload => {
console.log(payload)
});

// removing existing listener
this.$socket.$unsubscribe('event_name');
```
As an alternative, feel free to attach events directly to socket.io client, but keep in mind that you'd need to pass the same function to `.off(event_name, fn)` that you passed to `.on(event_name, fn)` in order to unsubscribe properly. Otherwise, [it won't work as you expect](https://github.com/probil/vue-socket.io-extended/issues/431#issuecomment-714377402).
```js
export default {
methods: {
onEventName(params) {
console.log('`eventName` has fired with:', params)
},
},
mounted() {
// subscribe
this.$socket.client.on('eventName', this.onEventName) // <-- this.onEventName here
},
beforeDestroy() {
// unsubscribe
this.$socket.client.off('eventName', this.onEventName) // <-- this.onEventName here
},
}
```

**Important**: Every dynamic subscription should have appropriate unsubscription. Or else, you'd experience [an event firing multiple times](https://github.com/probil/vue-socket.io-extended/issues/518). Moreover, unsubscribed leftovers might cause memory leaks.

#### Reactive properties (new in v4)
`$socket.connected` and `$socket.diconnected` are reactive. That means you can use them in expressions
```vue


{{ $socket.connected ? 'Connected' : 'Disconnected' }}

```
Or conditions
```vue


You are disconnected

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

## :evergreen_tree: 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:

```js
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](#gear-configuration) section if you'd like to use a custom transformation.

Check the [Migration from VueSocketIO](#information_source-migration-from-vuesocketio) section if you want to keep actions names in UPPER_CASE.

```js
// 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](https://socket.io/docs/client-api/#socket-emit-eventName-%E2%80%A6args-ack), including Buffer.

```js
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.

```js
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

## :bamboo: ECMAScript / TypeScript decorator (added in v4)

**Required**: [ECMAScript stage 1 decorators](https://github.com/wycats/javascript-decorators/blob/master/README.md).
If you use Babel, [babel-plugin-transform-decorators-legacy](https://github.com/loganfsmyth/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](https://github.com/vuejs/vue-class-component). 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:

```vue

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`
}
}

```

## :mountain_bicyclist: 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](https://github.com/ll931217) for investigation.

**1. Create plugin**:
```js
// ~/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**:

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

## :mountain_bicyclist: Usage with Quasar Framework
> Register vue-socket.io-extended with a boot file and disable server side rendering

**1. Create bootfile**:
```js
// ~/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**:

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

## :gear: 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 |
| eventMapping | `Function` `socket => string` | | Map your event from socket event data

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

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

## :information_source: Migration from VueSocketIO

For everyone who has migrated from old package VueSocketIO to this new one on existing project.
You need to re-define 2 parameters, in order to use existing store actions without changes (e.g. `SOCKET_EVENT_NAME`).

```js
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_', // (1) keep prefix in uppercase
eventToActionTransformer: (actionName) => actionName // (2) cancel camelcasing
});
```

## :question: FAQ

- [How to prevent connection until authed?](https://github.com/probil/vue-socket.io-extended/issues/114#issuecomment-405411500)

- [How to receive/emit event from server to the particular user?](https://github.com/probil/vue-socket.io-extended/issues/71#issuecomment-390820203) (check also [this](https://gitter.im/vue-socket-io-extended/Lobby?at=5bbc9973ef4afc4f2842d0bc))

- [How access this.$socket from Vuex actions?](https://github.com/probil/vue-socket.io-extended/issues/91#issuecomment-397232621)

- [My mutation is triggered two times?](https://github.com/probil/vue-socket.io-extended/issues/185)

- [Mutations and actions are not fired while using Quasar](https://github.com/probil/vue-socket.io-extended/issues/384#issuecomment-517736400)

## :anchor: Semantic Versioning Policy

This plugin follows [semantic versioning](http://semver.org/).

## :newspaper: Changelog

We're using [GitHub Releases](https://github.com/probil/vue-socket.io-extended/releases).

## :beers: 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](https://github.com/probil/vue-socket.io-extended/issues/new), but first please make sure your question does not repeat previous ones.

## :lock: License

See the [LICENSE](LICENSE) file for license rights and limitations (MIT).

[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fprobil%2Fvue-socket.io-extended.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fprobil%2Fvue-socket.io-extended?ref=badge_large)