【问题标题】:How to create a reusable Socket.IO module如何创建可重用的 Socket.IO 模块
【发布时间】:2016-09-12 18:35:15
【问题描述】:

我在创建一个公开 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) {
        // ...
      }
    }
  }
}

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

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

【问题讨论】:

  • 我认为您应该保持简单,并从服务器启动脚本(可能是您的项目中的 app.js/web.js 文件)公开io 实例并在您的模块中重用该实例。

标签: javascript node.js sockets dependency-injection socket.io


【解决方案1】:

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

  • 将对象设置为全局命名空间。 (改变全球需求关怀)
  • 使用 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) {
        // ...
      }
    }
  }
}

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

app.js

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

app.listen(80)

controller.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;
}

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

app.js

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

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

app.listen(80);

router.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);
};

controller.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;
}

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

app.js

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

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

app.listen(80);

controller.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;
}

myModule.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) {
        // ...
      }
    }
  }
}

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

【讨论】:

    【解决方案2】:

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

    全球

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

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

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

    导出

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

    module.exports.expressServerVar = server;

    只有我的 2 美分;你强烈不同意我的观点还是我错过了一些重要的事情?告诉我!

    【讨论】:

      【解决方案3】:

      我会使用工厂或依赖注入。你可以使用jimple之类的东西。

      但这是一个没有使用任何外部依赖项的示例。这绝不是最好的代码示例,但它应该可以理解这一点。我仍然建议使用 jimple 而不是这个。

      // app.js
      
      var express = require('express');
      var app = express();
      
      var factory = require('./factory.js');
      factory.setExpress(app); // This could also be done in the factory constructor. Or you could instanciate your express app in the factory.js class.
      
      
      // factory.js
      var socketIoModule = require('./your-socket-io-module.js')
      
      function Factory() {
      
      }
      
      Factory.prototype.setExpress = function(app) {
          this.app = app;
      }
      
      Factory.prototype.getSocketIOModule = function() {
          return socketIoModule(this.app);
      }
      
      // By exporting it this way we are making it a singleton
      // This means that each module that requires this file will
      // get the same instance of factory.
      module.exports = new Factory();
      
      
      // some code that needs socket io module
      var factory = require('./factory.js');
      
      function() {
          var socketIo = factory.getSocketIOModule();
      
          socketIo.doStuff();
      }
      

      【讨论】:

        【解决方案4】:

        我在应用程序中使用的方法是从启动脚本中公开 serverio 实例并在模块中重用它们

        // Setup servers.
        var http = require('http').Server(app);
        var io = require('socket.io')(http);
        
        // Setup application.
        require('./server/app')(app, express, io);
        
        // Start listening on port.
        http.listen(configs.PORT, function() {
            console.log("Listening on " + configs.PORT);
        });
        

        在您的模块中,您可以使用 io 实例来设置事件处理程序或发出事件,类似这样

        module.exports = {
          contest: function(io, contest) {
            var namespace = io.of('/' + contest.id);
            namespace.on('connection', function(socket) {
                socket.on('word', function(data) {
                    ...
                });
            });
          }
        };
        

        您的样品

        我会将此部分放在 app.js 或用于启动服务器的 js 文件中

        const sio = require('socket.io');
        const io = sio(server);
        

        并且会有这样的 Socket.IO 模块

        module.exports = function(server, io) {
          return {
            register: function(namespace) {
              let nsp = io.of(namespace);
              nsp.on('connect', function(socket) {
                // ...
              }
            }
          }
        }
        

        我的样本

        【讨论】:

          猜你喜欢
          • 2023-03-23
          • 1970-01-01
          • 2017-12-06
          • 2019-04-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-07-25
          • 1970-01-01
          相关资源
          最近更新 更多