==> 服务端促使客户端退出的小案例。

1 安装与配置

1.1 安装依赖包
# 1 egg 项目中安装
npm install egg-socket.io

# 2 vue 项目中安装
npm install vue-socket.io
1.2 配置 egg.js
config/config.default.js
module.exports = appInfo => {
  const config = exports = {};
  
  ......
  
  // 关闭 csrf
  config.security = {
    csrf: {
      enable: false,
    },
  };

  // cors 跨域配置
  config.cors = {
    origin: '*',
    // 允许请求的方法
    allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
  };
 	
  // ************** socket.io 配置 **************
  config.io = {
    init: {},
    namespace: { // 命名空间
      '/': { // 可通过 app.io.of('/') 获取到这个命名空间,下面代码中有用到
        // 对应下面的 app/io/middleware/connection.js
        connectionMiddleware: [ 'connection' ],
        // 对应下面的 app/io/middleware/packet.js
        packetMiddleware: [ 'packet' ],
      }
    }
  };
  // *******************************************
    
  return {
    ...config
  };
}
config/plugin.js
module.exports = {
  ......
  ......
  io: {
    enable: true,
    package: 'egg-socket.io',
  },
};
app/io/middleware/connection.js
/**
 * 在每一个客户端连接或者退出时发生作用
 * @param app
 * @returns {(function(*, *): Promise<void>)|*}
 */
module.exports = app => {
  return async (ctx, next) => {
    ctx.socket.emit('res', 'connected!');
    console.log('server socket connected');
    await next();
  };
};
app/io/middleware/packet.js
/**
 * 对消息进行预处理
 * @param app
 * @returns {(function(*, *): Promise<void>)|*}
 */
module.exports = app => {
  return async (ctx, next) => {
    ctx.socket.emit('res', 'packet received!');
    console.log('packet:', ctx.packet);
    await next();
  };
};

2 egg.js 中 websocket 使用

class AdminController extends Controller {
  ......
    // 其他代码
  ......

  /**
   * @description: 修改用户信息
   * @param {*}
   * @return {*}
   */
  async update() {
    const { ctx, app } = this;
    // =================== 引入 nsp,用于 websocket 相关操作 ===================
    const nsp = app.io.of('/'); // 获取到对应的命名空间的内容
    // =====================================================================

    let res;
    const reqBody = ctx.request.body;
    if (reqBody.type === 'rename') {
      // 重命名
      res = await ctx.service.admin.rename(ctx.params.id, reqBody);
    } else if (reqBody.type === 'editPassword') {
      // 重置密码
      res = await ctx.service.admin.editPassword(ctx.params.id, reqBody);
    } else if (reqBody.type === 'editStatus') {
      // 设置用户状态
      res = await ctx.service.admin.editStatus(ctx.params.id, reqBody);
      if (res) {
        if (reqBody.userStatus === 2 || reqBody.userStatus === 3) {
          // ====================== 发送消息 ======================
          nsp.emit('logout', { msg: 'logout', id: ctx.params.id });
          // ====================================================
        }
      }
    }

    if (res) {
      ctx.body = new SuccessResponse(null, '修改成功')
    } else {
      ctx.body = new ErrorResponse(null, '修改失败')
    }
  }
  ......
    // 其他代码
  ......
}

3 vue 中使用 websocket

main.js
// 引入 websocket
import VueSocketIO from 'vue-socket.io'

// 配置
const vueSocketIO = new VueSocketIO({
  debug: true,
  // 后端服务地址
  connection: 'http://127.0.0.1:7001',
})
// 监听connect事件
vueSocketIO.io.on('connect', () => {
  console.log('socket connect from main.js');
});
Vue.use(vueSocketIO)
home.vue
<script>
......
export default{
  sockets: {
    // 连接事件
    connect: function() {
      console.log("socket connect from HOME page");
    },
    // 登出事件
    logout: function(data) {
      console.log("wesocket-logout", data);
      if (data.id === sessionStorage.getItem("userId")) {
        this.$notify.error({
          title: "您的账号已被停用/注销/删除!"
        });
        console.log("我退出了");
        this.logout();
      }
    }
  },
  data(){...}
}
</script>

4 参考文献

[1] vue-socket.io使用教程与踩坑记录.

[2] egg-socket在egg中的使用.

Logo

前往低代码交流专区

更多推荐