如何创建可重用的Socket.IO模块

Mah*_*oni 7 javascript sockets dependency-injection node.js socket.io

我有一个创建模块的麻烦,该模块公开了我的Socket.IO库的功能:

const sio = require('socket.io');
module.exports = function(server) {
  const io = sio(server);
  return {
    register: function(namespace) {
      let nsp = io.of(namespace);
      nsp.on('connect', function(socket) {
        // ...
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在的问题是如何在其他模块中使用它?在我的app.js

我创建了serverwith Express并且可以require('./mysocketio')(server)在其他模块中实例化模块,因为服务器在那里不可用.解决这些循环依赖关系的好方法是什么?

Niv*_*esh 5

好吧,您可以通过各种方式实现这些目标,例如:

  • 将对象设置为全局命名空间。(改变全球需求关怀)
  • 使用 module.exports 并要求其他文件中的对象。(如果做得不好,可能会导致循环依赖问题)
  • 将实例作为参数传递给控制器​​,同时在路由中需要它们。

myModule.js 暴露 Socket.IO 库功能的模块

const sio = require('socket.io');
module.exports = function(server) {
  const io = sio(server);
  return {
    register: function(namespace) {
      let nsp = io.of(namespace);
      nsp.on('connect', function(socket) {
        // ...
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Flow 1:在全局命名空间中设置模块。

应用程序.js

var app = require('express').createServer();
var io = require('./myModule')(app);
global._io = io;

app.listen(80)
Run Code Online (Sandbox Code Playgroud)

控制器.js

module.exports = function(io){
    var that={};
    /*
     * Private local variable
     * made const so that 
     * one does not alter it by mistake
     * later on.
     */
    const _io = global._io; 

    that.myAction = function(req,res){

        _io.register('newRoom');
        res.send('Done');   
    }
    return that;
}
Run Code Online (Sandbox Code Playgroud)

流程 2:将模块作为参数传递。

应用程序.js

var app = require('express').createServer();
var io = require('./myModule')(app);

require(./router.js)(app,io);

app.listen(80);
Run Code Online (Sandbox Code Playgroud)

路由器.js

/*
 * Contains the routing logic
 */
module.exports = function (app,io) {
//passing while creating the instance of controller for the first time.
var controller = require("./controller")(io);

app.get('/test/about',controller.myAction);
};
Run Code Online (Sandbox Code Playgroud)

控制器.js

module.exports = function(io){
    var that={};

    const _io = io; 

    that.myAction = function(req,res){

        _io.register('newsRoom');
        res.send('Done');   
    }

    // everything attached to that will be exposed
    // more like making public member functions and properties.
    return that;
}
Run Code Online (Sandbox Code Playgroud)

流程 3:将 io 设置为全局。因此无需每次都通过服务器。

应用程序.js

var app = require('express').createServer();
require('./myModule')(app);

require(./router.js)(app);

app.listen(80);
Run Code Online (Sandbox Code Playgroud)

控制器.js

// no need to pass the server as io is already initialized
const _io = require('./myModule')();

module.exports = function(io){
    var that={};

    that.myAction = function(req,res){

        _io.register('newsRoom');
        res.send('Done');   
    }
    return that;
}
Run Code Online (Sandbox Code Playgroud)

我的模块.js

module.exports = function( server ) {

  const _io = global._io || require('socket.io')(server);

  if(global._io === undefined){
    //initializing io for future use
    global._io = _io;
  }

  return {
    register: function(namespace) {
      let nsp = _io.of(namespace);
      nsp.on('connect', function(socket) {
        // ...
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

可能最简洁的方法是将参数作为参数传递给控制器​​,同时在路由中要求它们。虽然第三个流程看起来很有希望,但在更改全局命名空间时应该小心。


Tin*_*ank 2

这并不是真正的循环依赖;只是您的模块 a) 依赖于另一个不是全局可用的模块,b) 您的模块可能在代码中的许多地方使用。

全球的

一个可能的解决方案(有缺点)是只加载一次模块,并将其附加到全局: global.mysocketio = require('./mysocketio')(server);

这允许您在加载 global.mysocketio 后在项目中的任何位置访问它。这是我个人用于自己的记录器构造的构造;我的记录器在我的代码周围的许多地方使用,所以我只是将其附加到 global.log。

然而,全局变量的使用有点肮脏;它带来了命名空间分离的问题(某些代码决定使用 global.mysocketio 本身),并且它创建了一个“不可见”的依赖关系;其他代码只是假设某个全局存在,并且找到这些依赖关系并不那么容易。

出口

更好的解决方案是将变量传递到需要的地方。有很多方法可以做到这一点。我知道您的 app.js 没有可用的服务器变量,但它肯定以某种方式包含您的快速代码。如果您需要 app.js 中提供的“服务器”或“mysocketio”,只需从您创建“服务器”的模块中导出它即可。喜欢:

module.exports.expressServerVar = server;

只是我的2分钱;你强烈不同意我的观点还是我错过了一些重要的事情?让我知道!