前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用原生js + websocket 做一个聊天室

使用原生js + websocket 做一个聊天室

作者头像
心念
发布2023-01-11 21:10:18
2K0
发布2023-01-11 21:10:18
举报
文章被收录于专栏:前端心念前端心念

前言

websocket是 html5 新增的一项api,实现客户端与服务器之间的即时通信。今天用它来实现一个聊天室demo,这里选择原生js来实现,因为用惯了vue和react的舒适框架,是时候复习一下原生的api了。毕竟现在前端技术更新很快,掌握好底层的东西才能做到以不变应万变

demo在线预览: http://101.42.108.39:90/chat/

思路

后台使用node搭建一个websocket服务器,客户端连接此服务器完成握手,前台每次发送消息,后台就向所有握手的客户端广播消息

关键api

前台

  • sorket = new WebSocket("ws://localhost:3000") 【初始化WebSocket对象】
  • this.sorket.onopen 【与服务端建立连接触发】
  • this.sorket.send 【向服务器发送消息】
  • this.sorket.onmessage 【收到服务端推送消息触发】

后台

  • ws.createServer=(conn=>{}).listen(3000) 【创建ws服务器】
  • conn.on("text", function (obj) {}) 【接收消息】
  • conn.sendText() 【向所有握手的客户端广播消息】

html部分

代码语言:javascript
复制
1<!DOCTYPE html>
2<html lang="en">
3  <head>
4    <meta charset="UTF-8" />
5    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7    <title>心念--云聊天室</title>
8    <link rel="stylesheet" href="./style.css" />
9  </head>
10  <body>
11    <div class="container">
12      <header>在线人数:0</header>
13      <main></main>
14      <footer>
15        <input
16          type="text"
17          placeholder="请输入要发送的内容..."
18          id="messageInput"
19        />
20        <button id="send">发 送</button>
21      </footer>
22
23      <div id="modal">
24        <div class="modal-content">
25          <h2>请输入昵称</h2>
26          <input type="text" />
27          <div><button>开始聊天</button></div>
28        </div>
29      </div>
30    </div>
31    <script src="./chat.js"></script>
32  </body>
33</html>
34

css样式

代码语言:javascript
复制
1* {
2  margin: 0;
3  padding: 0;
4}
5
6body,
7html {
8  height: 100%;
9}
10
11.container {
12  height: 100%;
13  display: flex;
14  flex-direction: column;
15}
16
17header {
18  height: 7vh;
19  border-bottom: 2px solid #555;
20  text-align: center;
21  line-height: 7vh;
22  font-size: 18px;
23  font-weight: bold;
24}
25
26main {
27  height: 85vh;
28  padding: 10px;
29  padding-bottom: 70px;
30  overflow: auto;
31}
32
33main .join-tip {
34  text-align: center;
35  color: #999;
36  margin: 5px;
37}
38
39main .mesItem {
40  position: relative;
41  display: flex;
42  padding: 25px 0;
43  color: #fff;
44}
45
46main .mesItem-me {
47  position: relative;
48  display: flex;
49  padding: 25px 0;
50  color: #fff;
51  justify-content: flex-end;
52}
53
54main .nickname {
55  width: 50px;
56  height: 50px;
57  border-radius: 50%;
58  background: #999;
59  text-align: center;
60  line-height: 50px;
61}
62main .content {
63  position: relative;
64  padding: 0 10px;
65  text-align: center;
66  line-height: 50px;
67  border-radius: 5px;
68}
69
70main p {
71  color: gray;
72  position: absolute;
73  bottom: -5px;
74}
75
76main .mesItem .content {
77  background-color: rgb(88, 179, 212);
78  margin-left: 15px;
79}
80
81main .mesItem-me .content {
82  background-color: rgb(21, 136, 21);
83  margin-right: 15px;
84}
85
86main .mesItem .content::before {
87  position: absolute;
88  left: -20px;
89  top: 50%;
90  transform: translateY(-50%);
91  height: 0;
92  width: 0;
93  content: "";
94  border: 10px solid rgba(255, 255, 255, 0);
95  border-top: 6px solid rgba(255, 255, 255, 0);
96  border-bottom: 6px solid rgba(255, 255, 255, 0);
97  border-right-color: rgb(88, 179, 212);
98}
99
100main .mesItem-me .content::before {
101  position: absolute;
102  right: -20px;
103  top: 50%;
104  transform: translateY(-50%);
105  height: 0;
106  width: 0;
107  content: "";
108  border: 10px solid rgba(255, 255, 255, 0);
109  border-top: 6px solid rgba(255, 255, 255, 0);
110  border-bottom: 6px solid rgba(255, 255, 255, 0);
111  border-left-color: rgb(21, 136, 21);
112}
113
114footer {
115  height: 8vh;
116  width: 100%;
117  display: flex;
118  border-top: 1px solid #999;
119  position: fixed;
120  bottom: 0;
121}
122#messageInput {
123  font-size: 18px;
124  border: none;
125  padding-left: 20px;
126  outline: none;
127  line-height: 8vh;
128  width: 100%;
129}
130#send {
131  width: 100px;
132  cursor: pointer;
133  background-color: aquamarine;
134  border-top: 1px solid #999;
135  font-size: 18px;
136}
137
138#modal {
139  height: 100%;
140  width: 100%;
141  background-color: rgba(0, 0, 0, 0.5);
142  position: fixed;
143  display: none;
144}
145
146#modal .modal-content {
147  border-radius: 5px;
148  padding: 10px;
149  background-color: #fff;
150  border: 1px solid #555;
151  height: 200px;
152  width: 300px;
153  position: absolute;
154  top: 50%;
155  left: 50%;
156  transform: translate(-50%, -50%);
157}
158
159#modal .modal-content h2 {
160  text-align: center;
161}
162
163#modal .modal-content input {
164  text-align: center;
165  width: 100%;
166  height: 40px;
167  margin: 15px 0;
168}
169
170#modal .modal-content button {
171  margin: 0 auto;
172  display: block;
173  background-color: aquamarine;
174  border: 1px solid #eee;
175  padding: 10px;
176  font-size: 18px;
177  cursor: pointer;
178}
179

js部分

代码语言:javascript
复制
1class Chat {
2  header = document.querySelector("header");
3  modal = document.querySelector("#modal");
4  modalInput = document.querySelector("#modal input");
5  modalButton = document.querySelector("#modal button");
6  msgInput = document.querySelector("#messageInput");
7  msgSendBtn = document.querySelector("#send");
8  main = document.querySelector("main");
9  user = {};
10  msg = "";
11  sorket = new WebSocket("ws://localhost:3000");
12  // sorket = new WebSocket("ws://101.42.108.39:3000");
13  msgList = [];
14
15  constructor() {
16    // 如果localstorge存在用户信息,直接引用
17    // 不存在,就弹窗注册
18    const user = localStorage.getItem("user");
19    if (!user) {
20      this.modal.style.display = "block";
21      this.modalButton.onclick = () => {
22        if (!this.modalInput.value) return alert("昵称不能为空");
23        const name = this.modalInput.value;
24        const uid = "chat_user_" + Date.now();
25        const userInfo = { name, uid };
26        this.user = userInfo;
27        // localStorage存一下
28        localStorage.setItem("user", JSON.stringify(userInfo));
29        this.modal.style.display = "none";
30
31        // 广播入场通知
32        this.send({ ...this.user, type: 1 });
33      };
34    } else {
35      this.user = JSON.parse(user);
36    }
37
38    // 消息输入与发送事件+回车发送事件
39    this.msgInput.oninput = (e) => {
40      this.msg = e.target.value;
41    };
42    this.msgSendBtn.onclick = () => {
43      if (!this.msg) return alert("不能发送空的内容");
44      this.send({ ...this.user, msg: this.msg, type: 2 });
45    };
46    document.onkeydown = (event) => {
47      var e = event || window.event;
48      if (e && e.keyCode == 13) {
49        //回车键的键值为13
50        if (!this.msg) return alert("不能发送空的内容");
51        this.send({ ...this.user, msg: this.msg, type: 2 });
52      }
53    };
54
55    this.sorket.onopen = () => {
56      console.log("连接服务器成功");
57
58      // 如果是注册过的用户,发送入场广播
59      if (this.user.name) this.send({ ...this.user, type: 1 });
60    };
61
62    // 消息接收监听
63    this.sorket.onmessage = (e) => {
64      let message = JSON.parse(e.data);
65      this.msgList.push(message);
66      this.render();
67    };
68  }
69
70  // 发送消息
71  send(data) {
72    this.sorket.send(JSON.stringify(data));
73    this.msgInput.value = "";
74    this.msg = "";
75  }
76
77  render() {
78    let html = "";
79    this.msgList.forEach(({ type, uid, name, msg, time, userTotal }) => {
80      if (type === 1) {
81        html += `<div class="join-tip">${name} 加入了聊天</div>`;
82      }
83
84      if (type === 2 && uid !== this.user.uid) {
85        html += `<div class="mesItem">
86            <div class="nickname">${name}</div>
87            <div class="content">${msg}</div>
88            <p>${time}</p>
89          </div>`;
90      }
91
92      if (type === 2 && uid === this.user.uid) {
93        html += ` <div class="mesItem-me">
94        <div class="content">${msg}</div>
95        <div class="nickname">${name}</div>
96        <p>${time}</p>
97      </div>`;
98      }
99
100      this.header.innerText = `在线人数:${userTotal}`;
101    });
102    this.main.innerHTML = html;
103
104    // 保持滚动到最底部
105    this.main.scrollTop = this.main.scrollHeight;
106  }
107}
108
109new Chat();
110

node部分

代码语言:javascript
复制
1const ws = require("nodejs-websocket");
2const moment = require("moment");
3
4var server = ws
5  .createServer(function (conn) {
6    conn.on("text", function (obj) {
7      obj = {
8        ...JSON.parse(obj),
9        time: moment().format("YYYY-MM-DD HH:mm:ss"),
10      };
11
12      // 连接用户数
13      obj.userTotal = server.connections.length;
14
15      // 发送广播
16      server.connections.forEach((conn) => {
17        conn.sendText(JSON.stringify(obj));
18      });
19    });
20
21    conn.on("close", function (code, reason) {
22      console.log("关闭连接");
23    });
24    conn.on("error", function (code, reason) {
25      console.log("异常关闭");
26    });
27  })
28  .listen(3000);
29console.log("WebSocket建立完毕");
30
本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 思路
  • 关键api
    • 前台
      • 后台
      • html部分
      • css样式
      • js部分
      • node部分
      相关产品与服务
      即时通信 IM
      即时通信 IM(Instant Messaging)基于腾讯二十余年的 IM 技术积累,支持Android、iOS、Mac、Windows、Web、H5、小程序平台且跨终端互通,低代码 UI 组件助您30分钟集成单聊、群聊、关系链、消息漫游、群组管理、资料管理、直播弹幕和内容审核等能力。适用于直播互动、电商带货、客服咨询、社交沟通、在线课程、企业办公、互动游戏、医疗健康等场景。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
      http://www.vxiaotou.com