天天看点

一文图解前端WebSocket 实时通信

💌 作者简介

  1. 📖 个人介绍:小伙伴们,大家好!我是水香木鱼,【​

    ​前端领域创作者​

    ​】😜
  2. 📢 人生箴言:即使没有万全准备,也要勇敢迈出第一步。
在​

​前端领域​

​​,​

​实时通信 WebSocket 是我们必须掌握的其中一个技术点​

​​,今天木鱼和大家通过案例,​

​快速的入门WebSocket 实时通信​

​。一🏍 届时我将代码部分,按照先后顺序,给大家通过文章的形式去分析。

木鱼也是刚刚开始了解​

​WebSocket​

​ ,如有不对的地方,请谅解。留言区,时刻恭候大家,我们共同迈进通信工程。

为了方便大家学习,本文只展示​

​UI组件库​

​​、​

​逻辑部分​

​​ | ​

​样式部分 不做展示​

​​ 小伙伴们可以去 👉​​案例源码​​ 获取。

1、什么是WebSocket?

根据百科给出的解释是:
  • WebSocket是一种在​

    ​单个TCP连接上​

    ​​进行​

    ​全双工通信​

    ​的协议。
  • WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。
  • WebSocket API也被​

    ​W3C定为标准​

    ​。
  • WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。

在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

其实一句话就可以做出概括:​

​它就是一种网络通信协议​

​,很多高级功能都需要它。

2、为什么需要 WebSocket?

初次接触 WebSocket 的人,都会问想一个问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它到底能带来什么好处?

答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。

比如说:我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。

3、特点

一文图解前端WebSocket 实时通信

​服务器​

​​可以​

​主动向客户端推送信息​

​​,​

​客户端​

​​也可以​

​主动向服务器发送信息​

​,是真正的双向平等对话,属于服务器推送技术的一种。

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

4、vue2案例

(1) 技术栈

首先我们使用的是 ​

​Ant Design of Vue 样式组件库​

①安装 ​

​ant-design-vue​

npm i --save ant-design-vue      
②在​

​main.js中​

​ 完整引入
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
Vue.use(Antd);      

(2) 目录结构

一文图解前端WebSocket 实时通信

(3)router.js配置

import Vue from 'vue'
import Router from 'vue-router'
//导入登录页
import Login from './views/Login.vue'

Vue.use(Router)

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    //登录页
    {
      path: '/Login',
      name: 'Login',
      component: Login
    },
    //首页
    {
      path: '/',
      name: 'Home',
      // route level code-splitting
      // this generates a separate chunk (about.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import(/* webpackChunkName: "Home" */ './views/Home.vue')
    }
  ]
})      

(4) 登录 【Login.vue】

一文图解前端WebSocket 实时通信

一💕登录布局:

在布局前,我们先构思好页面分布,良好的开端,要占据开发的一半。有思路,才会事半功倍。
一文图解前端WebSocket 实时通信
  • 聊天室标题
  • 用户名输入框
  • 进入聊天室按钮
  • 背景界面圆角 【样式处理】
①聊天室标题
<!--标题-->
<div class="boxs">
   <h1>木鱼全球·畅聊</h1>
</div>      
一文图解前端WebSocket 实时通信
②用户名输入框
<!--用户名输入框-->
 <a-input placeholder="请您输入用户名">
   <!-- 用户图标-->
   <a-icon slot="prefix" type="user" style="color: rgba(0, 0, 0, 0.25)"/>
 </a-input>      
一文图解前端WebSocket 实时通信
③进入聊天室按钮
<!--进入聊天室按钮-->
 <a-button type="dashed" ghost>
    <!--自定义图标-->
   <i class="iconfont icon-SEND" @click="添加进入聊天室事件👇"></i>进入聊天室
 </a-button>      

如何自定义图标请参考 👉​​如何引用阿里图标库​​

一💕登录逻辑:

①用户输入信息:在输入框 内绑定 ​

​v-model="username"​

在​

​data ​

​中定义存储用户信息的桶

data() {
    return {
      username: "",
    };
  },      
②进入聊天室事件:​

​@click="handleEnterBtnClick"​

//进入聊天室
    handleEnterBtnClick() {
     // trim() 去除用户名里的空格
      const username = this.username.trim();
      //如果 输入的用户名 少于3位,程序会给出提示,如果满足 则跳转至首页聊天室
      if (username.length < 3) {
        this.$message.warning("用户名不能小于3位", 1);
        return;
      }
      //将输入的用户名 保存到本地存储
      localStorage.setItem("username", username);
      //跳转到首页
      this.$router.push("/");
    },      
③针对于 本地里​

​有或者没有用户名信息​

如果有 进入首页聊天室,否则 返回登录页【首页返回的操作】
一文图解前端WebSocket 实时通信
一文图解前端WebSocket 实时通信
mounted() {
  //将用户信息存储在本地
    const username = localStorage.getItem("username");
    if (username) {
      this.$router.push("/");
      return;
    }
  },      
④清除本地存储当中的信息【演示效果】
一文图解前端WebSocket 实时通信

(5) 首页【Home.vue】

一文图解前端WebSocket 实时通信

一💕首页布局:

一文图解前端WebSocket 实时通信
一文图解前端WebSocket 实时通信
  • 消息列表
  • 内容输入框
  • 发送按钮
①消息列表:
<!--循环 列表-->
   <a-list ref="record" item-layout="horizontal" :data-source="msgList">
      <!--Ant 组件 默认的循环遍历数据结构  不可更改-->
      <a-list-item slot="renderItem" slot-scope="item">
        <a-list-item-meta>
          <p slot="title">
          <!--遍历用户名-->
            <span> {{ item.user }}</span>
           <!--发布时间--> 
            <span>发送时间:{{ $moment(item.dateTime).format("YYYY-MM-DD HH:mm:ss") }}</span>
          </p>
          <!--会话内容-->
          <p slot="title">会话内容:{{ item.msg }}</p>
          <!--用户头像显示-->
          <a-avatar slot="avatar" src="https://gw.alicdn.com/tps/TB1W_X6OXXXXXcZXVXXXXXXXXXX-400-400.png"/>
        </a-list-item-meta>
      </a-list-item>
    </a-list>      

附加:关于遍历的时间格式处理 请看:​​vue时间格式处理(YYYY-MM-DD HH:mm:ss)​​

②内容输入框:
<a-input placeholder="请输入消息"/>      
③发送按钮:
<a-button type="dashed" ghost>
  <i class="iconfont icon-SEND"></i>
  发送
</a-button>      

一💕首页逻辑【WebSocket默认配置】:

首先建立WebSocket 连接,我们要知道 WebSocket 在前端工程 支持 4种属性一👇

  • open 【建立连接】
  • close 【关闭连接】
  • error 【错误信息】
  • message 【接收处理】

附加了解内容:一🍾

后端的WebSocket 支持五种:
  1. open 【建立连接】
  2. close 【关闭连接】
  3. error 【错误信息】
  4. message 【连接处理】
  5. connection 【信息处理函数】

在外部作用域调用:

一文图解前端WebSocket 实时通信

详细WebSocket 服务链接部署 ,请看下面 ​​

​(6) node 通信部署​

​ 一🎈

//调用本地的WebSocket 服务
const ws = new WebSocket("ws://localhost:8000");      

此时需要vue2 项目位置–》下载​

​ws​

​​

一文图解前端WebSocket 实时通信
npm install ws -g      
①建立连接:
在mounted 生命周期内,写出 WebSocket 连接结构代码【以下属于原生写法】

注意:

  1. ​this.handleWsOpen​

    ​【作用于调用 methods中的连接函数事件】
  2. ​bind(this) ​

    ​ 【bind(this)的作用是改变this的指向】
②默认配置:
mounted() {
    //连接   
    ws.addEventListener("open", this.handleWsOpen.bind(this), false);
    //关闭
    ws.addEventListener("close", this.handleWsClose.bind(this), false);
    //错误
    ws.addEventListener("error", this.handleWsError.bind(this), false);
    //接收处理
    ws.addEventListener("message", this.handleWsMessage.bind(this), false);
  },      
③配置连接参数:
在methods 生命周期内 配置,它的默认参数
methods: {}      
//建立连接
handleWsOpen(e) {
   console.log("FE:WebSocket open", e);
},      
//关闭连接
handleWsClose(e) {
   console.log("FE:WebSocket close", e);
},      
//错误信息
handleWsError(e) {
   console.log("FE:WebSocket error", e);
},      

这步很重要,作用于前端接收广播数据 一🎯

//接收数据处理 默认结构
handleWsMessage(e) {
   console.log("FE:WebSocket message", e);
},      
④刷新处理函数事件:
我们需要在​

​mounted 生命周期​

​​中 添加 ​

​刷新页面处理函数​

判断本地存储当中有没有用户名,如果有用户名 点击浏览器刷新【F5】,不做跳转,否则跳转至登录页

mounted(){
   //将用户存储到本地
   this.username = localStorage.getItem("username");
    //判断 如果本地存储没有当前用户名 则 跳转回登录页
   if (!this.username) {
     this.$router.push("/Login");
     return;
   }
}      

(6) node通信部署

一文图解前端WebSocket 实时通信

一💕初始化处理

注意:在部署前,首先必须要做初始化处理

在server 目录下 运行 ​

​npm init -y​

​​初始化
一文图解前端WebSocket 实时通信
npm init -y      

一💕监听处理

使用nodemon 插件 做监听处理函数 一👆 也是在server 目录下运行
npm install nodemon -g      

一💕启用监听

在​

​package.json​

​​ 文件中修改 ​

​scripts​

​ 内的参数
  • dev 【运行方式,通过npm run dev】
  • nodemon index.js 【使用监听nodemon】、【引用index.js 文件】
"scripts": {
  "dev": "nodemon index.js"
},      
一文图解前端WebSocket 实时通信

一💕下载 ws 库【 附属于 WebSocket】

一👆 也是在server 目录下运行
npm install ws      

一💕新建index.js

①导入​

​const Ws = require('ws');​

const Ws = require('ws');
//建立模块
;((Ws) => {
  //......主要代码 在模块内写
})(Ws)      
②主要WebSocket配置【主要代码】
//接口 为8000
    const server = new Ws.Server({
        port: 8000
    });


    const init = () => {
        bindEvent()
    }

    function bindEvent() {
        server.on('open', handleOpen)
        server.on('close', handleClose)
        server.on('error', handleError)
        server.on('connection', handleConnection)
    }
    //与前端建立连接
    function handleOpen() {
        console.log('BE:WebSocket open');
    }
    //与前端关闭连接
    function handleClose() {
        console.log('BE:WebSocket close');
    }
    //错误信息
    function handleError() {
        console.log('BE:WebSocket error');
    }
    //信息处理
    function handleConnection(ws) {
        console.log('BE:WebSocket connection');
        ws.on('message', handleMessage)
    }
    //与前端建立连接
    function handleMessage(msg) {
        //遍历每一条内容信息
        server.clients.forEach((c) => {
            // 发送send 信息 到前端
            c.send(msg);
        })
    }      

(7) 首页添加触发事件函数

① 请输入消息框 绑定 ​

​v-model="msg"​

<a-input placeholder="请输入消息" v-model="msg"/>      
data() {
    return {
      msg: "",//发送的内容
      username: "",//用户名
      msgList: [],//消息列表数据
    };
  },      
②发送按钮绑定事件 ​

​@click="handleSendBtnClick"​

<a-button type="dashed" ghost @click="handleSendBtnClick">
  <i class="iconfont icon-SEND"></i>发送
</a-button>      
//发送逻辑
    handleSendBtnClick(e) {
      if (this.msg == "") {
        this.$message.warning("不能发送空白消息", 1);
      } else {
        const msg = this.msg;
        //去除头尾空格
        if (!msg.trim().length) {
          return;
        }
        //接收发送的内容
        ws.send(
          //将返回的数据以对象的形式 展示在前台
          JSON.stringify({
            id: new Date().getTime(),
            user: this.username,
            dateTime: new Date().getTime(),
            msg: this.msg,
          })
        );
        //输入完信息后 置空
        this.msg = "";
      }
    },      
③ 接收广播数据
在methods 中增加​

​handleWsMessage​

​ 处理函数 接收返回的数据
//前端接收广播数据
    handleWsMessage(e) {
      //数据类型转换 为对象的形式
      const msg = JSON.parse(e.data);    
      //将输入的内容 添加到 消息列表
      this.msgList.push(msg);

      console.log(msg);
      // console.log("FE:WebSocket message", e);
    },      

(8) 通信演示

继续阅读