feat(): Multiple namespaced connections

pull/288/head
Sherzod Aripjanov 4 years ago
parent 2f63eebb70
commit 35b3591b45

@ -89,8 +89,8 @@ new Vue({
debug|Boolean|`false`|Optional|Enable logging for debug
connection|String/Socket.io-client|`null`|Required|Websocket server url or socket.io-client instance
vuex.store|Vuex|`null`|Optional|Vuex store instance
vuex.actionPrefix|String|`null`|Optional|Prefix for emitting server side vuex actions
vuex.mutationPrefix|String |`null`|Optional|Prefix for emitting server side vuex mutations
vuex.actionPrefix|String/Function|`null`|Optional|Prefix for emitting server side vuex actions
vuex.mutationPrefix|String/Function|`null`|Optional|Prefix for emitting server side vuex mutations
#### 🌈 Component Level Usage
@ -180,6 +180,109 @@ export default new Vuex.Store({
})
```
#### 🏆 Connection Namespace
``` javascript
import Vue from 'vue'
import store from './store'
import App from './App.vue'
import VueSocketIO from 'vue-socket.io'
const app = SocketIO('http://localhost:1090', {
useConnectionNamespace: true,
});
const chat = SocketIO('http://localhost:1090/chat', {
useConnectionNamespace: true,
autoConnect: false,
});
Vue.use(new VueSocketIO({
debug: true,
connection: {
app,
chat
},
vuex: {
store,
actionPrefix: eventName => {
return (`SOCKET_` + eventName).toUpperCase();
},
mutationPrefix: eventName => {
return (`SOCKET_` + eventName).toUpperCase();
},
},
options: { path: "/my-app/" } //Optional options
}))
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
```
Then use it like this:
``` javascript
new Vue({
sockets: {
app: {
connect: function() {
console.log('socket connected');
},
customEmit: function(data) {
console.log('this method was fired by the socket server');
},
},
chat: {
connect: function() {
console.log('socket connected');
},
customEmit: function(data) {
console.log('this method was fired by the socket server');
},
},
},
methods: {
clickAppButton: function(data) {
// $socket.app is socket.io-client instance
this.$socket.app.emit('emit_method', data);
},
clickChatButton: function(data) {
// $socket.chat is socket.io-client instance
this.$socket.chat.emit('emit_method', data);
},
},
});
```
vuex
``` javascript
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {},
mutations: {
"<MUTATION_PREFIX>_<MY_NAMESPACE>_<EVENT_NAME>"() {
// do something
}
},
actions: {
"<ACTION_PREFIX>_<MY_NAMESPACE>_<EVENT_NAME>"() {
// do something
}
}
})
```
## Stargazers over time
[![Stargazers over time](https://starcharts.herokuapp.com/MetinSeylan/Vue-Socket.io.svg)](https://starcharts.herokuapp.com/MetinSeylan/Vue-Socket.io)

File diff suppressed because one or more lines are too long

14
index.d.ts vendored

@ -40,18 +40,24 @@ declare module 'vue/types/options' {
}
}
type prefixFunc = (eventName: string) => string;
type connectionSocket = {
[key: string]: SocketIOClient.Socket
}
export interface VueSocketOptions {
debug?: boolean;
connection: string | SocketIOClient.Socket,
connection: string | SocketIOClient.Socket | connectionSocket,
vuex?: {
store?: Store<any>,
actionPrefix?: string,
mutationPrefix?: string,
actionPrefix?: string | prefixFunc,
mutationPrefix?: string | prefixFunc,
options?: {
useConnectionNamespace?: boolean
}
},
// type declarations for optional options
// type declarations for optional options
options?:{
path?: string;
}

@ -1,136 +1,113 @@
import Logger from './logger';
export default class EventEmitter{
constructor(vuex = {}){
Logger.info(vuex ? `Vuex adapter enabled` : `Vuex adapter disabled`);
Logger.info(vuex.mutationPrefix ? `Vuex socket mutations enabled` : `Vuex socket mutations disabled`);
Logger.info(vuex ? `Vuex socket actions enabled` : `Vuex socket actions disabled`);
this.store = vuex.store;
this.actionPrefix = vuex.actionPrefix ? vuex.actionPrefix : 'SOCKET_';
this.mutationPrefix = vuex.mutationPrefix;
this.listeners = new Map();
export default class EventEmitter {
constructor(vuex = {}) {
Logger.info(vuex ? `Vuex adapter enabled` : `Vuex adapter disabled`);
Logger.info(vuex.mutationPrefix ? `Vuex socket mutations enabled` : `Vuex socket mutations disabled`);
Logger.info(vuex ? `Vuex socket actions enabled` : `Vuex socket actions disabled`);
this.store = vuex.store;
this.actionPrefix = vuex.actionPrefix ? vuex.actionPrefix : 'SOCKET_';
this.mutationPrefix = vuex.mutationPrefix ? vuex.mutationPrefix : 'SOCKET_';
this.listeners = new Map();
}
/**
* register new event listener with vuejs component instance
* @param event
* @param callback
* @param component
*/
addListener(event, callback, component) {
if (typeof callback === 'function') {
if (!this.listeners.has(event)) this.listeners.set(event, []);
this.listeners.get(event).push({ callback, component });
Logger.info(`#${event} subscribe, component: ${component.$options.name}`);
} else {
throw new Error(`callback must be a function`);
}
/**
* register new event listener with vuejs component instance
* @param event
* @param callback
* @param component
*/
addListener(event, callback, component){
if(typeof callback === 'function'){
if (!this.listeners.has(event)) this.listeners.set(event, []);
this.listeners.get(event).push({ callback, component });
Logger.info(`#${event} subscribe, component: ${component.$options.name}`);
} else {
throw new Error(`callback must be a function`);
}
}
/**
* remove a listenler
* @param event
* @param component
*/
removeListener(event, component) {
if (this.listeners.has(event)) {
const listeners = this.listeners.get(event).filter(listener => listener.component !== component);
if (listeners.length > 0) {
this.listeners.set(event, listeners);
} else {
this.listeners.delete(event);
}
Logger.info(`#${event} unsubscribe, component: ${component.$options.name}`);
}
/**
* remove a listenler
* @param event
* @param component
*/
removeListener(event, component){
if(this.listeners.has(event)){
const listeners = this.listeners.get(event).filter(listener => (
listener.component !== component
));
if (listeners.length > 0) {
this.listeners.set(event, listeners);
} else {
this.listeners.delete(event);
}
Logger.info(`#${event} unsubscribe, component: ${component.$options.name}`);
}
}
/**
* broadcast incoming event to components
* @param event
* @param args
*/
emit(event, args) {
if (this.listeners.has(event)) {
Logger.info(`Broadcasting: #${event}, Data:`, args);
this.listeners.get(event).forEach(listener => {
listener.callback.call(listener.component, args);
});
}
/**
* broadcast incoming event to components
* @param event
* @param args
*/
emit(event, args){
if(this.listeners.has(event)){
Logger.info(`Broadcasting: #${event}, Data:`, args);
this.listeners.get(event).forEach((listener) => {
listener.callback.call(listener.component, args);
});
}
if(event !== 'ping' && event !== 'pong') {
this.dispatchStore(event, args);
if (event !== 'ping' && event !== 'pong') {
this.dispatchStore(event, args);
}
}
/**
* dispatching vuex actions
* @param event
* @param args
*/
dispatchStore(event, args) {
if (this.store && this.store._actions) {
let prefixedEvent;
if (typeof this.actionPrefix === 'function') {
prefixedEvent = this.actionPrefix(event);
} else {
prefixedEvent = this.actionPrefix + event;
}
for (let key in this.store._actions) {
let action = key.split('/').pop();
if (action === prefixedEvent) {
Logger.info(`Dispatching Action: ${key}, Data:`, args);
this.store.dispatch(key, args);
}
}
}
if (this.store && this.store._mutations) {
let prefixedEvent;
if (typeof this.mutationPrefix === 'function') {
prefixedEvent = this.mutationPrefix(event);
} else {
prefixedEvent = (this.mutationPrefix || '') + event;
}
/**
* dispatching vuex actions
* @param event
* @param args
*/
dispatchStore(event, args){
if(this.store && this.store._actions){
let prefixed_event = this.actionPrefix + event;
for (let key in this.store._actions) {
let action = key.split('/').pop();
if(action === prefixed_event) {
Logger.info(`Dispatching Action: ${key}, Data:`, args);
this.store.dispatch(key, args);
}
}
for (let key in this.store._mutations) {
let mutation = key.split('/').pop();
if(this.mutationPrefix) {
let prefixed_event = this.mutationPrefix + event;
for (let key in this.store._mutations) {
let mutation = key.split('/').pop();
if(mutation === prefixed_event) {
Logger.info(`Commiting Mutation: ${key}, Data:`, args);
this.store.commit(key, args);
}
}
}
if (mutation === prefixedEvent) {
Logger.info(`Commiting Mutation: ${key}, Data:`, args);
this.store.commit(key, args);
}
}
}
}
}
}

@ -5,63 +5,49 @@ import Emitter from './emitter';
import SocketIO from 'socket.io-client';
export default class VueSocketIO {
/**
* lets take all resource
* @param io
* @param vuex
* @param debug
* @param options
*/
constructor({connection, vuex, debug, options}){
Logger.debug = debug;
this.io = this.connect(connection, options);
this.emitter = new Emitter(vuex);
this.listener = new Listener(this.io, this.emitter);
}
/**
* Vue.js entry point
* @param Vue
*/
install(Vue){
Vue.prototype.$socket = this.io;
Vue.prototype.$vueSocketIo = this;
Vue.mixin(Mixin);
Logger.info('Vue-Socket.io plugin enabled');
}
/**
* registering SocketIO instance
* @param connection
* @param options
*/
connect(connection, options){
if(connection && typeof connection === 'object'){
Logger.info('Received socket.io-client instance');
return connection;
} else if(typeof connection === 'string'){
Logger.info('Received connection string');
return this.io = SocketIO(connection, options);
} else {
throw new Error('Unsupported connection type');
}
/**
* lets take all resource
* @param io
* @param vuex
* @param debug
* @param options
*/
constructor({ connection, vuex, debug, options }) {
Logger.debug = debug;
this.io = this.connect(connection, options);
this.emitter = new Emitter(vuex);
this.listener = new Listener(this.io, this.emitter);
}
/**
* Vue.js entry point
* @param Vue
*/
install(Vue) {
Vue.prototype.$socket = this.io;
Vue.prototype.$vueSocketIo = this;
Vue.mixin(Mixin);
Logger.info('Vue-Socket.io plugin enabled');
}
/**
* registering SocketIO instance
* @param connection
* @param options
*/
connect(connection, options) {
if (connection && typeof connection === 'object') {
Logger.info('Received socket.io-client instance');
return connection;
} else if (typeof connection === 'string') {
Logger.info('Received connection string');
return (this.io = SocketIO(connection, options));
} else {
throw new Error('Unsupported connection type');
}
}
}

@ -1,27 +1,25 @@
export default class VueSocketIOListener {
/**
* socket.io-client reserved event keywords
* @type {string[]}
*/
static staticEvents = [
'connect',
'error',
'disconnect',
'reconnect',
'reconnect_attempt',
'reconnecting',
'reconnect_error',
'reconnect_failed',
'connect_error',
'connect_timeout',
'connecting',
'ping',
'pong'
'connect',
'error',
'disconnect',
'reconnect',
'reconnect_attempt',
'reconnecting',
'reconnect_error',
'reconnect_failed',
'connect_error',
'connect_timeout',
'connecting',
'ping',
'pong',
];
constructor(io, emitter){
constructor(io, emitter) {
this.io = io;
this.register();
this.emitter = emitter;
@ -30,22 +28,39 @@ export default class VueSocketIOListener {
/**
* Listening all socket.io events
*/
register(){
this.io.onevent = (packet) => {
register() {
if (this.io.constructor.name === 'Object') {
Object.keys(this.io).forEach(namespace => {
this.registerEvents(this.io[namespace], namespace);
});
} else {
this.registerEvents(this.io);
}
}
registerEvents(io, namespace) {
io.onevent = packet => {
let [event, ...args] = packet.data;
if(args.length === 1) args = args[0];
if (args.length === 1) args = args[0];
this.onEvent(event, args)
this.onEvent(this.getEventName(event, namespace), args);
};
VueSocketIOListener.staticEvents.forEach(event => this.io.on(event, args => this.onEvent(event, args)))
VueSocketIOListener.staticEvents.forEach(event =>
io.on(event, args => this.onEvent(this.getEventName(event, namespace), args)),
);
}
getEventName(event, namespace) {
return namespace ? namespace + '_' + event : event;
}
/**
* Broadcast all events to vuejs environment
*/
onEvent(event, args){
onEvent(event, args) {
console.log('onEvent', event);
this.emitter.emit(event, args);
}
}

@ -1,35 +1,27 @@
/**
* shitty logger class
*/
export default new class VueSocketIOLogger {
constructor() {
this.debug = false;
this.prefix = '%cVue-Socket.io: ';
}
info(text, data = '') {
if(this.debug) window.console.info(this.prefix+`%c${text}`, 'color: blue; font-weight: 600', 'color: #333333', data);
}
error() {
if(this.debug) window.console.error(this.prefix, ...arguments);
}
warn() {
if(this.debug) window.console.warn(this.prefix, ...arguments);
}
event(text, data = ''){
if(this.debug) window.console.info(this.prefix+`%c${text}`, 'color: blue; font-weight: 600', 'color: #333333', data);
}
}
export default new (class VueSocketIOLogger {
constructor() {
this.debug = false;
this.prefix = '%cVue-Socket.io: ';
}
info(text, data = '') {
if (this.debug)
window.console.info(this.prefix + `%c${text}`, 'color: blue; font-weight: 600', 'color: #333333', data);
}
error() {
if (this.debug) window.console.error(this.prefix, ...arguments);
}
warn() {
if (this.debug) window.console.warn(this.prefix, ...arguments);
}
event(text, data = '') {
if (this.debug)
window.console.info(this.prefix + `%c${text}`, 'color: blue; font-weight: 600', 'color: #333333', data);
}
})();

@ -1,56 +1,64 @@
export default {
/**
* Assign runtime callbacks
*/
beforeCreate(){
if(!this.sockets) this.sockets = {};
this.sockets.subscribe = (event, callback) => {
this.$vueSocketIo.emitter.addListener(event, callback, this);
};
this.sockets.unsubscribe = (event) => {
this.$vueSocketIo.emitter.removeListener(event, this);
};
},
/**
* Register all socket events
*/
mounted(){
if(this.$options.sockets){
Object.keys(this.$options.sockets).forEach(event => {
if(event !== 'subscribe' && event !== 'unsubscribe') {
this.$vueSocketIo.emitter.addListener(event, this.$options.sockets[event], this);
}
});
/**
* Assign runtime callbacks
*/
beforeCreate() {
if (!this.sockets) this.sockets = {};
this.sockets.subscribe = (event, callback) => {
this.$vueSocketIo.emitter.addListener(event, callback, this);
};
this.sockets.unsubscribe = event => {
this.$vueSocketIo.emitter.removeListener(event, this);
};
},
/**
* Register all socket events
*/
mounted() {
const { sockets } = this.$options;
const { emitter } = this.$vueSocketIo;
if (sockets) {
Object.keys(sockets).forEach(eventOrNamespace => {
const functionOrObject = sockets[eventOrNamespace];
if (
typeof functionOrObject === 'function' &&
eventOrNamespace !== 'subscribe' &&
eventOrNamespace !== 'unsubscribe'
) {
emitter.addListener(eventOrNamespace, functionOrObject, this);
} else {
Object.keys(functionOrObject).forEach(event => {
if (event !== 'subscribe' && event !== 'unsubscribe') {
emitter.addListener(`${eventOrNamespace}_${event}`, functionOrObject[event], this);
}
});
}
},
/**
* unsubscribe when component unmounting
*/
beforeDestroy(){
if(this.$options.sockets){
Object.keys(this.$options.sockets).forEach(event => {
this.$vueSocketIo.emitter.removeListener(event, this);
});
});
}
},
/**
* unsubscribe when component unmounting
*/
beforeDestroy() {
const { sockets } = this.$options;
const { emitter } = this.$vueSocketIo;
if (sockets) {
Object.keys(sockets).forEach(eventOrNamespace => {
const functionOrObject = sockets[eventOrNamespace];
if (typeof functionOrObject === 'function') {
emitter.removeListener(eventOrNamespace, functionOrObject, this);
} else {
Object.keys(functionOrObject).forEach(event => {
emitter.removeListener(event, functionOrObject[event], this);
});
}
});
}
}
},
};

Loading…
Cancel
Save