近来刚开始学node.js,基础,深入,express,koa,react,keystone,mongress,等等各种各样的关于Node.js的东西,每个都学的迷迷糊糊的,原因在于没有练习,感觉学的每一个知识点就像一块拼图,最终却没有拼到一起,于是就想多写几个小项目练练手,于是,就有了本文。。。

----------------------------------我是分割线-----------------------------------------------

恩,就按我写代码的顺序来记录一下我的小成果。

github地址:https://github.com/maichonglyd/mySmailChat.git 该项目是在本地运行

先看文件结构,其实也没几个文件:

mySmailChat

|---web

|   |---script

|   |    |---main.js

|   |---index.css

|   |---index.html

|---server.js

写代码总是有个先后顺序的,做项目也是,那先做哪一块后做哪一块就会对整个项目有那么些影响,我在想,一个网页聊天室,首先应该有的是网页,不论是聊天还是后台没有网页上的显示就不能准确的知道代码执行的结果,于是我就先做了网页的页面。

index.html:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>我的聊天室</title><link rel="stylesheet" type="text/css" href="./index.css">
</head>
<body><div class="container"><div class="titles"><h1 >my smail chat</h1></div><div class="chatValue" id="chatValue"></div><div class="sends"><input type="text" class="writeBox" placeHolder="请输入文本" id="chatMsg"></input><input type="button" class="sendButton" id="chatSendBtn" value="发送"></input></div></div><div id="loginPage"><p id="info">正在连接....</p><div id="nickWrapper"><input type="text" placeHolder="请输入昵称" id="nicknameInput" /><input type="button" value="确定" id="loginBtn" /></div></div><script src="/socket.io/socket.io.js"></script><script src="script/main.js"></script>
</body>
</html>

整个页面显示如下:

在页面上我用了npm仓库中的socket.io,使用socket来连接服务端,进行即时通信,另外使用了main.js来装载我们前端页面上的逻辑处理代码,呃,css样式还用说么?好吧!!下面是css样式的代码:

index.css

  body,html{background-color: #000;vertical-align: center;}div.container{width: 800px;height: 900px;margin-left: auto;margin-right: auto;background-color: #222;}div.titles{text-align: center;background-color: #333;color: #888;padding: 10px;}div.chatValue{width: 790px;margin:5px;height: 690px;overflow:auto;}div.sends{width: 100%;height: 100px;background-color: #333;}input.writeBox{height: 90px;width: 710px;margin-left: 3px;margin-top: 3px;margin-bottom: 3px;font-size: 20px;background-color: #333;border: 1px solid #222;color: #888;}input.sendButton{width: 78px;height: 94px;margin-top: 3px;margin-bottom: 3px;margin-right: 3px;float: right;border: 1px solid #222;background-color: #444;color: #888;font-size: 24px;}#loginPage{position: fixed;top: 0;left: 0;right: 0;bottom: 0;background-color: rgba(50,50,50,0.6);text-align: center;color: #888;display: block;padding-top: 400px;}.systemMsg{width: 100%;text-align: center;color: #f00;}

这里面的内容就不用多说了,到目前为止,前端页面已经做好了,下面就开始做服务端,先让这个页面能显示出来:

server.js:

const express = require('express');
const app = express();
const http = require('http').Server(app);
const io = require('socket.io')(http);let userList = [];http.listen(3000);
app.use("/",express.static(__dirname+"/web"));

本项目依赖的包有express, http, socket.io.

express是node.js中管理路由响应请求的模块,根据请求的URL返回相应的HTML页面。这里我们使用一个事先写好的静态页面返回给客户端,只需使用express指定要返回的页面的路径即可。如果不用这个包,我们需要将HTML代码与后台JavaScript代码写在一起进行请求的响应,不太方便。

http 是node.js的核心模块,不了解的同学就自己查一下吧,这里就不多说了。

socket.io 是用来做socket连接的模块,我们需要把这几个包相互绑定一下,于是就有了

const http = require('http').Server(app);
const io = require('socket.io')(http);

这两行代码,然后我们监听了3000端口,指定了要返回给前端的静态页面路径。

app.use("/",express.static(__dirname+"/web"));

在这里我们打开浏览器 输入 http://localhost:3000 就能看见我们写的界面了。当然输入昵称那个图应该不会太正,那个图的效果是需要连接上服务器进行一些js操作才会看起来更好看嘀,好吧,那我就需要先把服务器架起来,让前端页面可以连接上:

server.js:

io.on("connection",userConnction);
function userConnction(socket){console.log("a user connecting!!");let user = new User("",socket);userList.push(user);}class User{constructor(name,socket){this.nickName = name;this.socket = socket;}setName(name){this.nickName = name;}setSocket(){this.socket = socket;}getName(){return this.nickName;}getSocket(){return this.socket;}
}

io 监听连接成功事件,触发回调函数userConnection,服务端会输出一句话说明有用户连接上了。然后把这个用户先保存到数组里,既然是后台监听,那只有前端去主动连接了,好吧,那就前端主动连接:

main.js:

var nickName = "";
window.onload = function(){init();
}function init(){var info       = document.getElementById("info");
var nickWrapper = document.getElementById("nickWrapper");
var nicknameInput   = document.getElementById("nicknameInput");
var loginBtn        = document.getElementById("loginBtn");
var chatMsg     = document.getElementById("chatMsg");
var chatSendBtn = document.getElementById("chatSendBtn");
var loginPage       = document.getElementById("loginPage");info.textContent = "正在连接...."nickWrapper.style.display = "none";socket = io.connect("http://localhost:3000");socket.on("connect", function (){info.textContent = "请输入昵称!";nickWrapper.style.display = "block";nicknameInput.focus();});
}

希望上面那一片doucument.getElementById 没有让你看晕。我是把页面里用到的元素都先提前获取到了,下面就直接写获取后的变量名,不再写那么长了。

这里我们应该在网页刚加载完的时候就要先连接服务器,

socket = io.connect("http://localhost:3000");

连上之后把输入昵称那个图变成遮罩层盖住所有的操作区域,让用户输入名字,把焦点指向输入名字的文本框。

socket.on("connect", function (){......};

输入完名字之后我们需要把名字传到后台,应该在点击事件中完成:

main.js:

loginBtn.addEventListener("click",function (){var _nickName =  nicknameInput.value;if(_nickName.trim().length!=0){socket.emit("login",_nickName);nickName = _nickName;}else{nicknameInput.focus();nicknameInput.value = "";}},false);

点击事件中我们先获取文本框的值,去掉两端的空格之后如果长度不等于0那就说明输入的有内容,然后就向后台派发事件 login,再把名字传过去。同时我们声明一个全局变量nickName,记录下自己的名字。

socket.emit("login",_nickName);
nickName = _nickName;

既然我们向后台派发了事件,那后台应该要有接收的地方:

server.js:

socket.on("login",function (data){if(checkName(data)){socket.emit("login","ok");addName(data,socket);io.sockets.emit("system_login",data,userList.length);}else{socket.emit("login","have");}});function checkName(nickName){for(let i = 0,length=userList.length;i<length;i++){if(nickName == userList[i].nickName){return false;}}return true;
}
function addName(name,socket){for(let i = 0,length=userList.length;i<length;i++){if(userList[i].socket === socket){userList[i].nickName = name;console.log("nickName:",name);}}
}

后台监听前台派发的login事件,通过checkName函数来检查是否有重复名字,如果有就向前台派发login事件,带上参数"have",告诉前台名字已有,没有重复也派发lonin事件,带上参数"ok"告诉前台名字可用。然后用addName把名字记录下来。

socket.emit("login","ok");
addName(data,socket);

然后告诉所有的用户有新用户登录进来了.

io.sockets.emit("system_login",data,userList.length);

这里有个容易迷糊的地方:

login事件,是这样的,socket派发的事件只需要前后台对应就行,前台派发login,后台要有相应的接受,后台派发login,前台要有相应的接收,但前台派发login,前台就算有Login的接收也是没用的。

在后端通知前端名字可用派发了login事件,那前台要有相应的接收:

main.js:

socket.on("login",function (data){if(data == "ok"){loginPage.style.display = "none";document.title = "我的聊天室 | " + nickName;chatMsg.focus();}else if(data == "have"){info.textContent = "昵称被占用,请重新输入!";nicknameInput.value = "";nicknameInput.focus();}});

接收到以后,通过附带的参数来确定名字是否可用,可用就隐藏遮罩层,把自己的名字显示到网页的标题上,把焦点给发送信息的文本框,不可用就提示昵称被占用,清空输入名字的文本框,并给予焦点。

刚才后台还派发了一个事件,是告诉所有用户有新用户登录进来了,我们需要接收这个事件:

main.js:

socket.on("system_login",function (data,count){if(data == nickName){insertMsg("system_login","我","已加入聊天室,目前有" + count + "位用户在线");}else{insertMsg("system_login",data,"已加入聊天室,目前有" + count + "位用户在线");}});function insertMsg(type,name,msg){var color = "#f00";var space = ":";switch(type){case "system_login":case "system_logout":color="#f00";space = "";break;case "userChat":if(name == nickName){color = "#080";name = "我";}else{color = "#088";}space = ":<br/>&nbsp;&nbsp;"break;}var chatValue = document.getElementById("chatValue");var newNode = document.createElement("div");newNode.innerHTML = "<p width='790px' style='word-wrap:break-word'><font  color='"+color+"'>"+name +space+msg+"</font></p>";chatValue.appendChild(newNode);chatValue.scrollTop = chatValue.scrollHeight;}

这里我做了一个处理,如果新登录用户是自己的名字,那就显示 “我xxxx”;

这里我用insertMsg函数处理要显示在聊天框里的内容,其实就是通过判断不同的消息来源显示不同的字号,颜色及分割符,因为我们的聊天框是一个DIV,我们要往里面添加新的元素来显示每个人说的话,包括系统,

var chatValue = document.getElementById("chatValue");var newNode = document.createElement("div");newNode.innerHTML = "<p width='790px' style='word-wrap:break-word'><font  color='"+color+"'>"+name +space+msg+"</font></p>";chatValue.appendChild(newNode);

聊天记录是不断从下面添加,越是下面的记录,越是最新的,所以我们应该把滚动条设置到最底部:

chatValue.scrollTop = chatValue.scrollHeight;

既然有登录那就有退出,退出也会有一个事件,我们应该监听这个事件,在监听到有用户退出的时候后台应该告诉所有用户有人走了:

server.js:

socket.on("disconnect",userDisconnect);
function userDisconnect(){let i = 0;while(i<userList.length){if(userList[i].socket.disconnected){if(userList[i].nickName.trim().length!=0){io.sockets.emit("system_logout",userList[i].nickName,userList.length-1);}userList.splice(i,1);i--;}i++;}
}

这里我用循环来判断是否有用户已断开连接:

if(userList[i].socket.disconnected)

当有用户断开的话就发个通知告诉所有用户这个人离开了,还剩下多少人:

io.sockets.emit("system_logout",userList[i].nickName,userList.length-1);

好吧,后台又发通知了,前台也要接收:

main.js:

socket.on("system_logout",function (data,count){insertMsg("system_logout",data,"已离开聊天室,目前有" + count + "位用户在线");});

到目前为止,准备工做已经做的差不多了,还有最重要的一件事没做,那就是聊天,聊天内容需要我们点击按钮发送到服务器:

main.js:

chatSendBtn.addEventListener("click",function(){var _msg = chatMsg.value;if(_msg.trim().length !=0){socket.emit("userChat",_msg);}chatMsg.value = "";chatMsg.focus();});

发送的内容长度不算空格也不等于0的话,说明发送的是有真实内容的,那就派发给后台:

socket.emit("userChat",_msg);

同时要清空说话框并把焦点给它。

chatMsg.value = "";
chatMsg.focus();

后台,后台,快出来,我有话要对大家说:

server.js:

socket.on("userChat",function(data){for(let i = 0,length=userList.length;i<length;i++){if(socket === userList[i].getSocket()){let msgObj = {nickName : userList[i].getName(),msg : data};io.sockets.emit("userChat",msgObj);}}});  

后台脑子里记了很多人,需要知道你是谁,再把你要说的话和你的名字告诉大家,于是就需要检测socket对应的名字是谁然后连同发送的话一起推送给大家:

io.sockets.emit("userChat",msgObj);

呃,忘了解释一下:io.sockets.emit 是给所有连接上的客户端派发事件,某个socket.emit是给当前这个socket派发事件。

好吧,又是前台要接收:

main.js:

socket.on("userChat",function (data){insertMsg("userChat",data.nickName,data.msg);});

到了这里,项目基本上完成了。看看效果吧,

转载于:https://blog.51cto.com/wuzishu/1740439

node.js学习笔记之简洁聊天室相关推荐

  1. 千锋Node.js学习笔记

    千锋Node.js学习笔记 文章目录 千锋Node.js学习笔记 写在前面 1. 认识Node.js 2. NVM 3. NPM 4. NRM 5. NPX 6. 模块/包与CommonJS 7. 常 ...

  2. node.js学习笔记

    # node.js学习笔记标签(空格分隔): node.js---## 一 内置模块学习 ### 1. http 模块 ``` //1 导入http模块 const http =require('ht ...

  3. node.js学习笔记14—微型社交网站

    node.js学习笔记14-微型社交网站 1.功能分析 微博是以用户为中心,因此需要有注册和登录功能. 微博最核心的功能是信息的发表,这个功能包括许多方面,包括:数据库访问,前端显示等. 一个完整的微 ...

  4. Node.js学习笔记8

    Node.js学习笔记8 HTTP服务器与客户端 Node.js的http模块,封装了一个高效的HTTP服务器和一个简易的HTTP客户端 http.server是一个基于事件的HTTP服务器,核心由N ...

  5. node.js学习笔记5——核心模块1

    node.js学习笔记5--核心模块1 Node.js核心模块主要内容包括:(1)全局对象 (2)常用工具 (3)事件机制 (4)文件系统访问 (5)HTTP服务器与客户端 一: 全局对象 Node. ...

  6. node.js 学习笔记(二)模板引擎和C/S渲染

    node.js 学习笔记(二)模板引擎和C/S渲染 文章目录 node.js 学习笔记(二)模板引擎和C/S渲染 一.初步实现Apache功能 1.1 使用模板引擎 1.2 在 node 中使用模板引 ...

  7. 唤醒手腕 - 前端服务器端开发 Node.Js 学习笔记(学习中,更新中)

    唤醒手腕 - Node.Js 学习笔记 唤醒手腕个人的学习记录,时间在2021年12月13日 ~ 2021年12月14日,学习方式看官方文档和B站视频,如有错误或者代码问题的地方,欢迎C站大佬能够帮忙 ...

  8. node.js学习笔记Day2

    目录 第一部分:用npm安装mysql模块 第二部分:在项目内创建 第三部分:引用opreateDB方法 第四部分:解决异步方法的问题 第五部分:关于热启动 第六部分:关于接收参数和带参查询数据 今天 ...

  9. node.js学习笔记 - 文件上传(并用七牛云托管)

    文章目录 环境搭建 准备工作 安装相关依赖 代码实现 执行 环境搭建 准备工作 提示:本文采用ts来构建环境,要是以js构建则取掉类型定义即可. 初始化项目 创建目录fileUpload-demo- ...

最新文章

  1. 求解N个值中最大的k个数,N远大于k
  2. 全球唯一:MySQL社区2018年度公司贡献奖颁给阿里云
  3. Andorid 刷新样式一
  4. TCP/IP协议精华指南pdf发布
  5. Luogu P2577 [ZJOI2005]午餐
  6. Asp.net mvc 知多少(一)
  7. linux 修改网卡报错xe,centos修改端口出现Failed to start OpenSSH server daemon 启动报错和-xe报错的解决方法...
  8. C++函数模板5分钟入门
  9. 柒上支付个人免签支付系统源码
  10. UIView的一些基本方法 init、loadView、viewDidLoad、viewDidUnload、dealloc
  11. python基础之内置异常对象
  12. 北理珠计算机学院罗晓莹,“职”等你来 | 计算机职业发展中心2020年见面大会,我们如期相遇~...
  13. 个人理财软件CheckBook Pro for Mac
  14. CAD对话框不见后要如何调出
  15. 方舟生存进化服务器存档位置,方舟生存进化怎么转移存档
  16. 基于遗传算法解决城市TSP问题
  17. 麻雀算法SSA,优化VMD,适应度函数为最小包络熵,包含MATLAB源代码,直接复制粘贴!
  18. 一行代码实现IOS 3DES加密解密
  19. 基于自定义注解校验入参Model中的必传字段
  20. Android项目——电话拨号器

热门文章

  1. gee微端服务器系统设置,geem2微端服务器设置
  2. 食谱生成器与营养计算器的核心部分设计
  3. 程序员、架构师、技术经理、技术总监、CTO,怎么定位?
  4. Python练习:炉石传说荣誉室返尘最优策略
  5. html 网站发布到公网
  6. Android之光线传感器
  7. CSS Gird布局用法
  8. pyhton——爬小说网站(顶点最强国防生)
  9. 【前端】在vue项目中使用mixpanel记录用户访问量,5s内同一客户端记录一次
  10. 【数学解析几何】C_几种常见的函数曲线——(典型曲线图)