如何收听WebSocket消息并实时弹出示例代码

457 阅读3分钟

本文主要介绍了可以监听WebSocket消息并实时弹出的前端示例代码。这个例子假设你已经掌握了react js并熟悉umiJS。

1.项目要求

  1. 服务器端向前端推送消息,消息需要显示在网页的右下角。
  2. 根据不同的消息类型,提供不同的操作按钮,如 "同意"和 "拒绝"。

2.代码设计

  1. 使用WebSocket来建立通道。
  2. 前端基于umi+antd+重新连接websocket.js来开发。
  3. 使用express + express-ws + mockjs创建WebSocket服务通道,模拟服务器推送的消息。

3.使用方法

  1. 项目需要导入 reconnecting-websocket.min.js,具体可以看其官方文档。

  2. 登录成功后,调用WebSocket进行初始化。

    yield put({
        type: 'websocket/init',
        payload: {
            authToken
        }
    });
    

4.WebSocket服务组件源代码

  1. /service/websocket.js

    /**
     * Import reconnecting-websocket library.
     * Encapsulate service file.
     */
    class Websocket{
     
      /**
       * websocket logic
       */
     
      constructor(){
        this.websocket=null;
        this.url='ws://127.0.0.1:30001/websocket-im';
        this.options={
          connectionTimeout: 5000,
          maxRetries: 10,
        };
      }
     
      init=()=>{
        this.websocket = new ReconnectingWebSocket(this.url,[], this.options);
      }
     
      close=()=>{
        this.websocket && this.websocket.close();
      }
     
      onMessage=(callback)=>{
        this.websocket && this.websocket.addEventListener('message', (e) => {
          callback&&callback(e)
        });
      }
     
    }
     
    const websocket = new Websocket();
     
    // Initialize connection.
    export function openWs() {
      return websocket.init();
    }
     
    // Close connection.
    export function closeWs() {
      return websocket.close();
    }
     
    // listen websocket message.
    export function onMessage() {
      let deferred;
      websocket.onMessage(function(e){
        if(deferred) {
            deferred.resolve(e)
            deferred = null 
        }
      });
      return {
        message() {
          if(!deferred) {
              deferred = {}
              deferred.promise = new Promise(resolve => deferred.resolve = resolve)
          }
          return deferred.promise;
        }
      }
    }
    
  2. /model/websocket.js

     
    /**
     * Encapsulating model files
     * You should learn the components moment、immutable、antd、nanoid by yourself.
     */
    import {openWs,onMessage,closeWs} from 'services/websocket'
    import moment from 'moment'
    import { Map, fromJS } from 'immutable'
    import { notification } from 'antd'
    import nanoid from 'nanoid';
     
    const initState = Map({
     
      message:Map(), // received message.
      
    });
    export default {
      namespace: 'websocket',
     
      state: initState,
      subscriptions: {
        setup({ dispatch, history }) {
          dispatch({
            type: 'listener'
          });
          return history.listen(({ pathname, query }) => {
            
          });
        },
      },
      effects: {
     
        * listener({ payload }, { take, put, call }) {
          while (true) {
            const { type, payload } = yield take(['logout']);
            
            // Close the websocket if the message type is 'logout'.
            if (type === 'logout') {
              // Close websocket
              yield call(closeWs);
              notification.destroy();
              yield put({
                type: 'clearAllMessage', 
                payload:{
                }
              });
            }
          }
        },
     
        // Start websocket
        * init ({
          payload,
        }, { put, call, select }) {
          yield call(openWs);
          const listener = yield call(onMessage);
          yield put({type: 'receiveMsg', payload:{listener}});
        }, 
     
        // receive the message.
        * receiveMsg ({
            payload: {listener}
        }, { call, select, put}) {
            while(true){
              const event = yield call(listener.message);
     
              yield put({
                type: 'progressMsg', 
                payload:{
                  msg:JSON.parse(event.data)
                }
              });
              
                
            }
        },
     
        // process the message.
        * progressMsg ({
            payload: {msg}
        }, { call, select, put}) {
     
          console.log(msg)
          
          yield put({
            type: 'addOneMessage', 
            payload:{
              msg
            }
          });
            
        },
     
      },
      
      reducers: {
        
        addOneMessage(state, { payload:{msg} }) {
       
          const msgId = nanoid()+'-'+moment().format('x');
          return state.setIn(['message',msgId], fromJS({...msg,msgId}))
     
        },
     
        removeOneMessage(state, { payload:{msgId} }) {
       
          return state.deleteIn(['message',msgId])
     
        },
     
        clearAllMessage(state, { payload:{} }) {
       
          return state.setIn(['message'],Map())
     
        },
        
     
      },
      
    }
    

5.通知组件文件结构,源代码

  1. 通知组件结构

    Notyification
       - Demo1.js
       - Demo2.js
       - index.js
       - index.less
       - package.json
    
  2. Demo1.js

    import React from 'react'
    import styles from './index.less'
     
    export default class NotificationSon extends React.Component {
      
      render(){
        const {note,intl:{formatMessage}} = this.props;
        let content=note.getIn(['content']);
     
        return(
            <div className={styles.Notification}>
              <div>{content}</div>
            </div>
        )
      }
      
    }
     
    NotificationSon.propTypes = {
      
    }
    
  3. Demo2.js

    import React from 'react'
    import styles from './index.less'
    import { config } from 'utils'
    import { Button } from 'antd';
     
    const { defaultStyleSize } = config;
     
    export default class NotificationSon extends React.Component {
     
      dealApproval=(type,data)=>{
        const {dispatch,onClose} = this.props;
        if(type=='refuse'){
          console.log('Reject')
          onClose();
        }else if(type=='agree'){
          console.log('Agree')
          onClose();
        }
        
      }
      
      render(){
        const {note,intl:{formatMessage}} = this.props;
        let content=note.getIn(['content']);
     
        return(
            <div className={styles.Notification}>
              <div>{content}</div>
              <ul className={styles.btns}>
                <li>
                  <Button style={{ marginLeft: '12px' }} type={'primary'} size={defaultStyleSize}  onClick={() => {this.dealApproval('agree',note.get('data'))}}>{formatMessage({id: 'Global.agree'})}</Button>
                </li>
                <li>
                  <Button style={{ marginLeft: '12px' }} type={'danger'} size={defaultStyleSize}  onClick={() => {this.dealApproval('refuse',note.get('data'))}}>{formatMessage({id: 'Global.refuse'})}</Button>
                </li>
              </ul>
            </div>
        )
      }
      
    }
     
    NotificationSon.propTypes = {
      
    }
    
  4. index.js

    /**
     * Source code of pop-up window in the lower right corner.
     */
    import React from 'react'
    import { injectIntl } from 'react-intl';
    import moment from 'moment'
    import { connect } from 'dva'
    import { notification } from 'antd';
    import Demo1 from './Demo1'
    import Demo2 from './Demo2'
     
    @injectIntl
    @connect(({
      websocket, 
    }) => ({ 
      websocket
    }))
    export default class Notification extends React.Component {
     
      componentWillReceiveProps(nextProps) {
        const {websocket,dispatch,intl, intl: { formatMessage }} = nextProps;
        let message=websocket.get('message');
     
        message.forEach((note)=>{
     
          let object=note.getIn(['object']);
          let msgId=note.getIn(['msgId']);
          let title=note.getIn(['title']);
          let content=note.getIn(['content']);
          let format = 'YYYY-MM-DD HH:mm:ss';
          let time=note.getIn(['ts'])?moment(note.getIn(['ts']), 'x').format(format):moment().format(format);
     
          switch (object) {
            case 'demo1':
              content=<Demo1
                            dispatch={dispatch}
                            intl={intl}
                            note={note}
                            onClose={()=>this.onClose(msgId)}
                        />;
                        break;
            case 'demo2':
              content=<Demo2
                dispatch={dispatch}
                intl={intl}
                note={note}
                onClose={()=>this.onClose(msgId)}
              />;
              break;
            default:
                        break;
                }
     
          notification.open({
            message: <span>{title} <small>{time}</small></span>,
            duration:30,
            key: msgId,
            description:content,
            placement: 'bottomRight',
            onClick: () => {
              
            },
            onClose: () => {
              this.onClose(msgId);
            }
          });
        })
     
      }
     
      // If message is closed.
      onClose=(msgId)=>{
        const {dispatch} = this.props;
        dispatch({
          type:'websocket/removeOneMessage',
          payload:{
            msgId
          }
        })
        return notification.close(msgId);
      }
      
      render(){
        return(
            null
        )
      }
      
    }
     
     
    Notification.propTypes = {
      
    }
    
  5. index.less

    .Notification{
        .btns{
            padding: 0;
            margin: 15px 0 0 0;
            list-style: none;
            width: 100%;
            display: flex;
            justify-content: flex-end;
            li{
                margin-left: 10px;
            }
        }
    }
    
  6. package.json

    {
      "name": "Notification",
      "version": "0.0.0",
      "private": true,
      "main": "./index.js"
    }
    
  7. mockup message by express.js.

    let data = Mock.mock({
        
        "object|1":["demo1", "demo2"],
        "ts":new Date().getTime(),
        "title":"Message Title@string('number', 2)",
        "content":"You have a new message, @csentence(4, 10)",
        "data":{
            "audit_id":2,
            "host_ip":"9.9.9.9",
            "protocol":"ssh",
            "port":22,
            "conn_id":"3"
        }
    })
    
    ws.send(JSON.stringify(data))
    
    setTimeout(()=> {
    
         sendFunc()
    }, 3000)