JavaScript与Node.js一起打造一款聊天App

聊天是我们人与人交流最直接的方式,互联网的加入使我们交流更加便捷。我们手机上的微信、QQ是我们手机必不可少的应用软件。那么,我们是否可以做一款聊天应用呢?

之前我自己闲着没事,研究过一些技术,做了一款即时通讯应用,下面我将选取几幅具有代表性的图片供大家参考。

一、应用示图

以上是这款应用的主要页面,功能可能相对简陋点,不过基本的功能已经实现了,下面我将给出核心代码,全部源码地址在文末。

二、部分核心源码

前台主要核心逻辑:

这里我只列举了js核心代码,查看完整代码可以去文末。

1function sock() { 2 return io.connect("http://localhost:3003"); // http环境下 3} 4// 心跳机制 5document.addEventListener('visibilitychange', function () { 6 if (document.visibilityState == 'hidden') { 7 //记录页面隐藏时间 8 sock() 9 console.log('隐藏了') 10 } 11}) 12setInterval(() => { 13 sock() 14}, 10000); 15var socket = sock() 16var re = document.querySelector("#re"); 17var register1 = document.querySelector(".register"); 18var init = document.querySelector(".init"); 19var passr = document.querySelector("#passr"); 20var passl = document.querySelector("#passl"); 21var login1 = document.querySelector(".login"); 22var register_b = document.querySelector("#register_b"); 23var lo = document.querySelector("#lo"); 24var chat = document.querySelector("#chat"); 25var login_b = document.querySelector("#login_b"); 26var myMes = ""; 27var vf = ""; 28var na = ""; 29var p = ""; 30var we = ""; 31var div = ""; 32var v = ""; 33var q = 0; 34var regCn = /[@:]/im; 35var pattern = /^[\u4E00-\u9FA5]{1,5}$/; 36// 同意 37document.querySelector('.yes').onclick=function () { 38 document.querySelector('.dark').style.display='none' 39} 40document.querySelector('.ys').onclick = function () { 41 document.querySelector('.dark').style.display = 'block' 42} 43// 初始页面注册 44document.querySelector("#reg").onclick = function () { 45 register1.style.display = "block"; 46 init.style.display = "none"; 47 document.querySelector(".bg").style.display = "none"; 48} 49// 初始页面登录 50document.querySelector("#log").onclick = function () { 51 login1.style.display = "block"; 52 init.style.display = "none"; 53 document.querySelector(".bg").style.display = "none"; 54} 55// 登录按钮 56login_b.onclick = function () { 57 login(); 58 59} 60// 注册按钮 61register_b.onclick = function () { 62 register(); 63} 64//发送 65document.getElementById("btn").onclick = function () { 66 send(); 67}; 68// 内容填充 69document.getElementById("text").onkeyup = function () { 70 if (document.getElementById("text").value.length != 0) { 71 document.getElementById("btn").style.cssText = "background:#98E165;color:#fff;" 72 } else { 73 document.getElementById("btn").style.cssText = "background: #DDDEE2;color:#fff" 74 } 75} 76document.querySelector("#text").onclick = function () { 77 document.querySelector('#text').scrollIntoView(false); 78} 79// 传名 80var users2 = ""; 81socket.on('users', function (users) { 82 users2 = users; 83 // console.log(users2); 84}); 85// 传密码 86var pass2 = "" 87socket.on('pass', function (val) { 88 pass2 = val; 89 // console.log(pass2) 90}); 91// 统计在线人数 92var arrh = [] 93socket.on('dataval', function (val) { 94 vf = val; 95 console.log(vf); 96 97 for (let i = 0; i < vf.length; i++) { 98 // uu++ 99 arrh.push(vf[i]) 100 console.log(arrh) 101 } 102 var rf = [...new Set(arrh)] 103 console.log(rf) 104 rf = vf 105 for (let j = 0; j < rf.length; j++) { 106 var li = document.createElement("li"); 107 li.classList.add("active"); 108 li.innerText = rf[j] 109 console.log(rf[j]) 110 socket.emit("time", rf[j]); 111 document.querySelector(".fix").appendChild(li); 112 113 } 114}); 115socket.on('join', function (val) { 116 document.querySelector(".fix").innerHTML = '' 117}) 118socket.on('disconnect', function (val) { 119 console.log('离开了') 120 document.querySelector(".fix").innerHTML = '' 121}) 122// 生成数组 123var ar = ""; 124socket.on('array', function (val) { 125 ar = val; 126 // console.log(ar); 127}); 128// 封装注册 129function register() { 130 if (re.value.length == 0) { 131 sweetAlert("请输入用户名!"); 132 return false; 133 } else if (regCn.test(re.value)) { 134 sweetAlert("格式错误,不能够用和:符号取名,请重新输入!"); 135 return false; 136 } else if (pattern.test(re.value)) { 137 sweetAlert("不能使用中文字符哦!"); 138 return false; 139 } else if (!(re.value.length == 0 && regCn.test(re.value))) { 140 if (users2.indexOf(re.value) != -1) { 141 sweetAlert("已经注册啦,换一个用户名吧!"); 142 } else { 143 names(re.value.trim()); 144 pass(passr.value.trim()); 145 sweetAlert("注册成功,您的用户名:" + re.value.trim()); 146 document.querySelector(".swal-button").onclick = function () { 147 window.location.reload(); 148 } 149 } 150 } 151} 152//移动端使用touchend 153var event = navigator.userAgent.match(/(iPhone|iPod|Android|ios)/i) ? 'touchend' : 'click'; 154 155// 选择器 156var Q = function (id) { 157 return document.getElementById(id) 158}; 159//右 160var _right = new mSlider({ 161 dom: ".layer-right", 162 direction: "right" 163}); 164 165Q("btnRight").addEventListener(event, function (e) { 166 _right.open(); 167}) 168// 封装登录 169function login() { 170 if (lo.value.length == 0) { 171 sweetAlert("请输入用户名!"); 172 return false; 173 } else if (regCn.test(lo.value)) { 174 sweetAlert("格式错误,不能够用和:符号取名,请重新输入!"); 175 return false; 176 } else if (pattern.test(lo.value)) { 177 sweetAlert("不能使用中文字符哦!"); 178 return false; 179 } else if (!(lo.value.length == 0 && regCn.test(lo.value))) { 180 if (users2.indexOf(lo.value) != -1) { 181 for (var i = 0; i < users2.length; i++) { 182 if (users2[i] === lo.value && pass2[i] === passl.value) { 183 if (ar.indexOf(lo.value) == -1) { 184 sweetAlert("恭喜您,登录成功!"); 185 socket.emit('setName', lo.value.trim()); 186 names1(lo.value.trim()); 187 login1.style.display = "none"; 188 document.querySelector(".bg").style.display = "none"; 189 document.querySelector(".cd span").style.display = "none"; 190 document.querySelector(".title img").style.display = "block"; 191 document.querySelector(".fix").style.display = "block"; 192 document.querySelector(".title").style.display = "block"; 193 _right.open(); 194 document.querySelector(".swal-button").onclick = function () { 195 document.getElementById("text").focus(); 196 document.querySelector(".fix").addEventListener('click', function (e) { 197 if (e.target.nodeName === "LI" && e.target.innerText != document.title) { 198 _right.close(); 199 document.querySelector(".chat_b").style.display = "block"; 200 document.querySelector(".box").style.display = "block"; 201 document.querySelector(".tit").innerText = e.target.innerText; 202 document.querySelector(".ys").style.display="none"; 203 document.querySelector("#text").focus(); 204 onOff = true; 205 } else { 206 sweetAlert("不能跟自己聊天哦~"); 207 } 208 }) 209 } 210 } else { 211 sweetAlert("不能重复登录哦!"); 212 return 213 } 214 } 215 if (users2[i] === lo.value && pass2[i] != passl.value) { 216 sweetAlert("密码错误!"); 217 return; 218 } 219 } 220 } else { 221 sweetAlert("请先注册哦!"); 222 login1.style.display = "none"; 223 register1.style.display = "block"; 224 } 225 } 226} 227// 传名 228function names(value) { 229 this.name = value; 230 socket.emit("reg", name); 231} 232 233function names1(value) { 234 this.name1 = value; 235 socket.emit("join", name1); 236 document.title = name1 237} 238// 传密码 239function pass(value) { 240 socket.emit("pass", value); 241} 242socket.on("join", function (user) { 243 this.na = user; 244}) 245socket.on("reg", function (user) { 246 this.na1 = user; 247}) 248// 私发消息 249socket.on('message1', function (data) { 250 var p1 = document.createElement("div"); 251 var s1 = document.createElement("p"); 252 var s2 = document.createElement("p"); 253 var div1 = document.createElement("div"); 254 var em = document.createElement("em"); 255 var ads = document.createElement("audio"); 256 ads.src = "https://www.maomin.club/data/res.mp3"; 257 ads.className = "ads"; 258 s1.className = "chatlist"; 259 s2.className = "chatlist1"; 260 em.className = "zwasked1"; 261 div1.className = "divbox"; 262 s1.innerText = data.from; 263 s2.innerText = data.msg; 264 s1.appendChild(em); 265 p1.appendChild(s1); 266 p1.appendChild(s2); 267 chat.appendChild(ads); 268 ads.play(); 269 div1.appendChild(p1); 270 chat.appendChild(div1); 271 chat.scrollTop = chat.scrollHeight; 272}); 273// 私聊发送 274function send() { 275 if (document.getElementById("text").value != "") { 276 socket.emit('sayTo', { 277 from: lo.value, 278 to: document.querySelector(".tit").innerText, 279 msg: document.querySelector("#text").value, 280 }) 281 var p1 = document.createElement("div"); 282 var s1 = document.createElement("p"); 283 var s2 = document.createElement("p"); 284 var em = document.createElement("em"); 285 var div1 = document.createElement("div"); 286 var ads = document.createElement("audio"); 287 p1.style.cssText = "float:right;"; 288 s2.style.cssText = "color:#333;" 289 ads.src = "https://www.maomin.club/data/s.wav"; 290 ads.className = "ads"; 291 div1.className = "divbox"; 292 s1.className = "chatlist"; 293 s1.style.cssText = "color:#333 !important;float:right; !important"; 294 s2.className = "chatlist2"; 295 em.className = "zwasked"; 296 s1.innerText = lo.value; 297 s2.innerText = document.querySelector("#text").value; 298 s1.appendChild(em); 299 p1.appendChild(s1); 300 p1.appendChild(s2); 301 chat.appendChild(ads); 302 ads.play(); 303 div1.appendChild(p1); 304 chat.appendChild(div1); 305 chat.scrollTop = chat.scrollHeight; 306 } else { 307 sweetAlert('请输入内容!'); 308 } 309 chat.scrollTop = chat.scrollHeight; 310 document.querySelector("#text").value = ""; 311 document.querySelector("#text").focus(); 312} 313

后台主要核心逻辑:

我这里只列举了http环境的,完整代码中有https环境的。

1var http=require("http"); 2var fs=require("fs"); 3var express = require('express'); 4var ws=require("socket.io"); 5var path=require("path"); 6var _ = require('underscore'); 7var usocket = []; 8var usocket1 = []; 9var pass=[]; 10var data=[]; 11var hashName = {}; 12var onlineCount = 0; 13var app = express(); 14// 静态文件识别 15app.use(express.static(path.join(__dirname, './public'))); 16var server=http.createServer(function (req,res) { 17 var filename = req.url.split('/')[req.url.split('/').length-1]; 18 var suffix = req.url.split('.')[req.url.split('.').length-1]; 19 if(req.url==='/'){ 20 res.writeHead(200, {'Content-Type': 'text/html'}); 21 var html = fs.readFileSync("./public/index.html"); 22 res.end(html) 23 }else if(suffix==='css'){ 24 res.writeHead(200, {'Content-Type': 'text/css'}); 25 res.end(get_file_content(path.join(__dirname, 'public', 'css', filename))); 26 }else if(suffix==='js') { 27 res.writeHead(200, {'Content-Type': 'text/javascript'}); 28 res.end(get_file_content(path.join(__dirname, 'public', 'js', filename))); 29 }else if (suffix in ['gif', 'jpeg', 'jpg', 'png']) { 30 res.writeHead(200, { 31 'Content-Type': 'image/' + suffix 32 }); 33 res.end(get_file_content(path.join(__dirname, 'public', 'images', filename))); 34 } 35}); 36function get_file_content(filepath) { 37 return fs.readFileSync(filepath); 38} 39// 获取在线 40function broadcast() { 41 io.sockets.emit("dataval", hashName); 42} 43//提供私有socket 44function privateSocket(toId) { 45 return (_.findWhere(io.sockets.sockets, { 46 id: toId 47 })); 48} 49// 封装删除 50function removeByValue(arr, val) { 51 for (var i = 0; i < arr.length; i++) { 52 if (arr[i] == val) { 53 arr.splice(i, 1); 54 break; 55 } 56 } 57} 58// 连接socket 59var io=ws(server); 60io.on("connection",function(socket){ 61// 写入成功后读取测试 62fs.readFile('./user.xls', 'utf-8', function (err, data) { 63 if(data!=null){ 64 var value = data.split('\n'); 65 io.sockets.emit("users", value); 66 } 67 68}); 69// 写入成功后读取测试 70fs.readFile('./password.xls', 'utf-8', function (err,data) { 71 if(data!=null){ 72 var pass1=data.split('\n'); 73 io.sockets.emit("pass", pass1); 74 } 75}); 76 broadcast(); 77// 生成名字 78socket.on('setName', function (data) { 79 var name = data; 80 hashName[name] = socket.id; 81 // console.log(hashName[name]); 82 broadcast(); 83}); 84// 私聊发送 85socket.on('sayTo', function (data) { 86 var toName = data.to; 87 var toId; 88 console.log(toName); 89 if (toId = hashName[toName]) { 90 privateSocket(toId).emit('message1', data); 91 } 92}); 93// 离开 94socket.on('disconnect', function (name) { 95 name=this.i2; 96 io.emit("disconnect", name); 97 removeByValue(data, name); 98 io.sockets.emit("dataval", data); 99 }) 100// 在线 101socket.on('time', function (val) { 102 // console.log(val); 103 }) 104// 注册 105socket.on("reg", function (name) { 106 usocket[name] = socket; 107 this.i1=name; 108 io.emit("reg", name); 109 var myname =this.i1+"\n"; 110 fs.writeFile('./user.xls', myname, { 111 'flag': 'a' 112 }, function (err) { 113 if (err) { 114 throw err; 115 } 116 // 写入成功后读取测试 117 fs.readFile('./user.xls', 'utf-8', function (err,data) { 118 if (err) { 119 throw err; 120 } 121 }); 122 }); 123 }) 124// 加入 125io.emit('connected', ++onlineCount); 126 // console.log(data); 127 io.sockets.emit("array", data); 128 socket.on("join", function (name) { 129 usocket1[name] = socket; 130 this.i2 = name; 131 io.emit("join", name); 132 data.push(name); 133 io.sockets.emit("dataval", data); 134 }) 135// 密码 136socket.on("pass",function(val){ 137 pass[val]=socket; 138 this.i2=val; 139 io.emit("pass", val); 140 var password=this.i2+"\n"; 141 fs.writeFile('./password.xls', password, { 142 'flag': 'a' 143 }, function (err) { 144 if (err) { 145 throw err; 146 } 147 }); 148 }) 149}); 150server.listen(3003); 151console.log("服务器运行中");

三、源码地址

这个项目是之前写的,欢迎大家进行指正。大家可以复制下面的源码地址,拉取下来就可以在本地实现一个聊天服务。如果你有服务器可以把它部署在服务器上,这样你就可以有一个属于自己的聊天App了。大家可以根据源码进行学习,有不明白的可以随时问我。

https://github.com/maomincoding/chat3
  • 欢迎关注我的公众号前端历劫之路

  • 回复关键词电子书,即可获取12本前端热门电子书。

  • 回复关键词红宝书第4版,即可获取最新《JavaScript高级程序设计》(第四版)电子书。

  • 关注公众号后,点击下方菜单即可加我微信,我拉拢了很多IT大佬,创建了一个技术交流、文章分享群,期待你的加入。

  • 作者:Vam的金豆之路

  • 微信公众号:前端历劫之路

本文转自 https://mp.weixin.qq.com/s/mvpHMZqHYKA6jr1QfixQxA,如有侵权,请联系删除。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Android So动态加载 优雅实现与原理分析

背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我