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

2023-01-11 21:10:18 浏览数 (1)

前言

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

0 人点赞