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 debug|Boolean|`false`|Optional|Enable logging for debug
connection|String/Socket.io-client|`null`|Required|Websocket server url or socket.io-client instance connection|String/Socket.io-client|`null`|Required|Websocket server url or socket.io-client instance
vuex.store|Vuex|`null`|Optional|Vuex store instance vuex.store|Vuex|`null`|Optional|Vuex store instance
vuex.actionPrefix|String|`null`|Optional|Prefix for emitting server side vuex actions vuex.actionPrefix|String/Function|`null`|Optional|Prefix for emitting server side vuex actions
vuex.mutationPrefix|String |`null`|Optional|Prefix for emitting server side vuex mutations vuex.mutationPrefix|String/Function|`null`|Optional|Prefix for emitting server side vuex mutations
#### 🌈 Component Level Usage #### 🌈 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
[![Stargazers over time](https://starcharts.herokuapp.com/MetinSeylan/Vue-Socket.io.svg)](https://starcharts.herokuapp.com/MetinSeylan/Vue-Socket.io) [![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

12
index.d.ts vendored

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

@ -1,14 +1,13 @@
import Logger from './logger'; import Logger from './logger';
export default class EventEmitter { export default class EventEmitter {
constructor(vuex = {}) { constructor(vuex = {}) {
Logger.info(vuex ? `Vuex adapter enabled` : `Vuex adapter disabled`); Logger.info(vuex ? `Vuex adapter enabled` : `Vuex adapter disabled`);
Logger.info(vuex.mutationPrefix ? `Vuex socket mutations enabled` : `Vuex socket mutations disabled`); Logger.info(vuex.mutationPrefix ? `Vuex socket mutations enabled` : `Vuex socket mutations disabled`);
Logger.info(vuex ? `Vuex socket actions enabled` : `Vuex socket actions disabled`); Logger.info(vuex ? `Vuex socket actions enabled` : `Vuex socket actions disabled`);
this.store = vuex.store; this.store = vuex.store;
this.actionPrefix = vuex.actionPrefix ? vuex.actionPrefix : 'SOCKET_'; this.actionPrefix = vuex.actionPrefix ? vuex.actionPrefix : 'SOCKET_';
this.mutationPrefix = vuex.mutationPrefix; this.mutationPrefix = vuex.mutationPrefix ? vuex.mutationPrefix : 'SOCKET_';
this.listeners = new Map(); this.listeners = new Map();
} }
@ -19,20 +18,14 @@ export default class EventEmitter{
* @param component * @param component
*/ */
addListener(event, callback, component) { addListener(event, callback, component) {
if (typeof callback === 'function') { if (typeof callback === 'function') {
if (!this.listeners.has(event)) this.listeners.set(event, []); if (!this.listeners.has(event)) this.listeners.set(event, []);
this.listeners.get(event).push({ callback, component }); this.listeners.get(event).push({ callback, component });
Logger.info(`#${event} subscribe, component: ${component.$options.name}`); Logger.info(`#${event} subscribe, component: ${component.$options.name}`);
} else { } else {
throw new Error(`callback must be a function`); throw new Error(`callback must be a function`);
} }
} }
/** /**
@ -41,12 +34,8 @@ export default class EventEmitter{
* @param component * @param component
*/ */
removeListener(event, component) { removeListener(event, component) {
if (this.listeners.has(event)) { if (this.listeners.has(event)) {
const listeners = this.listeners.get(event).filter(listener => listener.component !== component);
const listeners = this.listeners.get(event).filter(listener => (
listener.component !== component
));
if (listeners.length > 0) { if (listeners.length > 0) {
this.listeners.set(event, listeners); this.listeners.set(event, listeners);
@ -55,9 +44,7 @@ export default class EventEmitter{
} }
Logger.info(`#${event} unsubscribe, component: ${component.$options.name}`); Logger.info(`#${event} unsubscribe, component: ${component.$options.name}`);
} }
} }
/** /**
@ -66,71 +53,61 @@ export default class EventEmitter{
* @param args * @param args
*/ */
emit(event, args) { emit(event, args) {
if (this.listeners.has(event)) { if (this.listeners.has(event)) {
Logger.info(`Broadcasting: #${event}, Data:`, args); Logger.info(`Broadcasting: #${event}, Data:`, args);
this.listeners.get(event).forEach((listener) => { this.listeners.get(event).forEach(listener => {
listener.callback.call(listener.component, args); listener.callback.call(listener.component, args);
}); });
} }
if (event !== 'ping' && event !== 'pong') { if (event !== 'ping' && event !== 'pong') {
this.dispatchStore(event, args); this.dispatchStore(event, args);
} }
} }
/** /**
* dispatching vuex actions * dispatching vuex actions
* @param event * @param event
* @param args * @param args
*/ */
dispatchStore(event, args) { dispatchStore(event, args) {
if (this.store && this.store._actions) { if (this.store && this.store._actions) {
let prefixedEvent;
let prefixed_event = this.actionPrefix + event; if (typeof this.actionPrefix === 'function') {
prefixedEvent = this.actionPrefix(event);
} else {
prefixedEvent = this.actionPrefix + event;
}
for (let key in this.store._actions) { for (let key in this.store._actions) {
let action = key.split('/').pop(); let action = key.split('/').pop();
if(action === prefixed_event) { if (action === prefixedEvent) {
Logger.info(`Dispatching Action: ${key}, Data:`, args); Logger.info(`Dispatching Action: ${key}, Data:`, args);
this.store.dispatch(key, args); this.store.dispatch(key, args);
} }
}
} }
if(this.mutationPrefix) { if (this.store && this.store._mutations) {
let prefixedEvent;
let prefixed_event = this.mutationPrefix + event; if (typeof this.mutationPrefix === 'function') {
prefixedEvent = this.mutationPrefix(event);
} else {
prefixedEvent = (this.mutationPrefix || '') + event;
}
for (let key in this.store._mutations) { for (let key in this.store._mutations) {
let mutation = key.split('/').pop(); let mutation = key.split('/').pop();
if(mutation === prefixed_event) { if (mutation === prefixedEvent) {
Logger.info(`Commiting Mutation: ${key}, Data:`, args); Logger.info(`Commiting Mutation: ${key}, Data:`, args);
this.store.commit(key, args); this.store.commit(key, args);
} }
} }
} }
}
} }
} }

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

@ -1,6 +1,4 @@
export default class VueSocketIOListener { export default class VueSocketIOListener {
/** /**
* socket.io-client reserved event keywords * socket.io-client reserved event keywords
* @type {string[]} * @type {string[]}
@ -18,7 +16,7 @@ export default class VueSocketIOListener {
'connect_timeout', 'connect_timeout',
'connecting', 'connecting',
'ping', 'ping',
'pong' 'pong',
]; ];
constructor(io, emitter) { constructor(io, emitter) {
@ -31,21 +29,38 @@ export default class VueSocketIOListener {
* Listening all socket.io events * Listening all socket.io events
*/ */
register() { register() {
this.io.onevent = (packet) => { 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; 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 * Broadcast all events to vuejs environment
*/ */
onEvent(event, args) { onEvent(event, args) {
console.log('onEvent', event);
this.emitter.emit(event, args); this.emitter.emit(event, args);
} }
} }

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

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