Next.js通过Socket.io和redis实现消息推送模块(二)next通过socket.io实现广播系统消息(比如‘网站维护中’)

1,142 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言:

最近在完成大三的一个课程期末设计,独立完成做了一个博客社区,主要技术栈是:

前端:next.js + mobx + ts + antd;

后台管理系统:vue3.0 + pinia + ts + elementUI

后端:next.js + ts + 阿里云oss存储 + redis

开发的时候遇到了很多坑,后面会通过文章的方式总结自己在开发过程中踩到的坑以及一些小经验。

由于博客社区嘛,像咱们大掘金平台,是有系统消息、点赞消息啥的等信息推送,然后我也想要做一个横幅(就是系统维护中,或者是出了什么问题,想要用户感知到,所以就想做一个系统消息推送),一开始通过websocket来实现,但是next.js这个框架似乎不太支持,而是支持了一个封装websocket的socket.io这个第三方依赖。所以就使用了socket.io来进行消息推送和在线人数统计的操作,希望大家喜欢~

一、需求

先看看最终实现的效果~ QQ录屏20220519232955_.gif

说明:在后台管理系统中输入通知的主题和内容,就可以实时的在前端展示出通知给用户了

二、(后端)socket.io实现广播通信

如果要实现socket的广播通知的话,通过:

socket.broadcast.emit('notification', {
  message: data
})

也就是添加了一个broadcast

// 参考medium的代码
const app = require('express')()
const server = require('http').Server(app)
const io = require('socket.io')(server, {cors:true})
const next = require('next')

const dev = process.env.NODE_ENV !== 'production';
const hostname = 'localhost';
const port = 3000;
const nextApp = next({ dev, hostname, port })
const nextHandler = nextApp.getRequestHandler()

let socketPort = 3000

io.on('connect', socket => {
  socket.on('clientOnline', () => {
    console.log('用户上线了')
  })
  socket.on('disconnect', () => {
    console.log('用户关闭了')
  })
  // 广播通知
  socket.on('notification', data => {
    socket.broadcast.emit('notification', {
      message: data
    })
  })
})

nextApp.prepare().then(() => {
  app.get('*', (req, res) => {
    return nextHandler(req, res)
  })
  app.post('*', (req, res) => {
    return nextHandler(req, res)
  })

  server.listen(socketPort, err => {
    if (err) throw err
    console.log(`socket io ready on http://localhost:${port}`)
  })
})

说明:上面的代码中,我们通过监听notification事件给server.js里面的socket.io服务器,而服务器接收的就是我们在后台管理系统中发送的notification事件

三、给所有用户推送系统消息(比如网站维护中)

直接上代码,之后再解释,let is go next.js的server

const app = require('express')()
const server = require('http').Server(app)
const io = require('socket.io')(server, {cors:true})
const next = require('next')

const dev = process.env.NODE_ENV !== 'production';
const hostname = 'localhost';
const port = 3000;
const nextApp = next({ dev, hostname, port })
const nextHandler = nextApp.getRequestHandler()

let socketPort = 3000

io.on('connect', socket => {
  socket.on('clientOnline', () => {
    console.log('用户上线了')
  })
  socket.on('disconnect', () => {
    console.log('用户关闭了')
  })
  // 广播通知
  socket.on('notification', data => {
    socket.broadcast.emit('notification', {
      message: data
    })
  })
})

nextApp.prepare().then(() => {
  app.get('*', (req, res) => {
    return nextHandler(req, res)
  })
  app.post('*', (req, res) => {
    return nextHandler(req, res)
  })

  server.listen(socketPort, err => {
    if (err) throw err
    console.log(`socket io ready on http://localhost:${port}`)
  })
})

后台系统代码(我用的是vue3写的,所以下面就是vue3中使用socket的方法了)

<template>
  <div>
      // 省略代码
  </div>
</template>

<script lang='ts' setup>
import { ref } from 'vue'
import io from 'socket.io-client'

const socket = ref(io('http://localhost:3000'))

// ... 省略其他代码

const handleFormSuccess = (notification) => {
  socket.value.emit('notification', {
      title: '通知主题',
      content: '通知内容'
  })
}
</script>

<style lang='scss' scoped></style>

我们在后台管理系统中发送emit notification,然后在next.js的server.js建立的socket里面进行监听notification事件

server.js的关键代码:

// 广播通知
// data 为 { title: '通知主题', content: '通知内容' }
  socket.on('notification', data => {
    socket.broadcast.emit('notification', {
      message: data
    })
  })

之后就是next.js的前端代码了,

在next.js的layout组件中,关于为什么要在这个组件中写代码的理由已经在第一个模块中juejin.cn/post/709946… 讲解了~

import { useEffect } from "react";
import { observer } from "mobx-react-lite"
import io from 'socket.io-client'
import { notification } from 'antd';

var socket : any
const Layout = ({ children } : any) => {
  const clineOnline = () => {
    socket.emit('clientOnline', '用户id')
  }

const sendNotification = (title, content) => {
    notification.open({
      message: title,
      description: content
    });
  } 
  
useEffect(() => {
    if (!socket) {
      socket = io('http://localhost:3000')
    }
    document.addEventListener('visibilitychange',function(){
      var isHidden = document.hidden;
      if(!isHidden){
        clineOnline()
      }
    })
    
    // 关键代码
    socket.on('notification', data => {
      const { title, content }  = data.message
      sendNotification(title, content)
    })
 }, [])
   
return (
    <div>
        Layout container
    </div>
  )
}

export default observer(Layout)

我们在next.js前端代码中通过监听notification事件,使用antd提供的notification这个组件进行消息通知

image.png

下一节我将分享《next.js中通过socket.io结合redis来统计当前网站的在线人数》希望大家喜欢