零基础实现node+express个性化聊天室的示例
本篇文章使用node+express+jquery写一个个性化聊天室,一起来get一下~(源码地址见文章末尾)
效果图
项目结构
- 登录检测
- 系统自动提示用户状态(进入/离开)
- 显示在线用户
- 支持发送和接收消息
- 自定义字体颜色
- 支持发送表情
- 支持发送图片
下面将一一讲解如何实现
前期准备
、、
具体实现
1、将聊天室部署到服务器
先用node搭建一个服务器,部署在localhost:3000端口,先尝试向浏览器发送一个“hello world”,新建server.js文件。
app.get('/',function(req,res){ // 路由为localhost:3000时向客户端响应“hello world”
res.send('
Hello world
'); // 发送数据
});
http.listen(3000,function(){ // 监听3000端口
console.log('listening on *:3000');
});
打开浏览器输入网址:localhost:3000是这样的
一个node服务器搭建成功。
接下来用express向浏览器返回一个html页面
将server.js的代码改一下:
// 路由为/默认www静态文件夹
app.use('/',express.static(__dirname + '/www'));
express.static(__dirname + '/www');是将www文件夹托管为静态资源,意味着这个文件夹里的文件(html、css、js)彼此可以用相对路径。在www文件夹中添加index.html文件以及相应的css(相应css代码就不贴了,详情见源码),如下,该页面用了font-awesome小图标
打开localhost:3000,会看到如下:
聊天室成功部署到服务器。
2、检测登录
在客户端和服务器之间传送消息需要用到socket.io
将server.js改动如下:
app.use('/',express.static(__dirname + '/www'));
io.on('connection',function(socket){ // 用户连接时触发
console.log('a user connected');
});
http.listen(3000,function(){
console.log('listening on *:3000');
});
当打开localhost:3000的时候会触发服务器端io的connection事件,会在服务器打印“a user connected”,但是我们想统计一下连接该服务器的用户人数,如果有用户连接就打印“n users connected”,n为用户人数,怎么办呢?
在server.js设置一个全局数组为user,每当一个用户连接成功就在连接事件中将用户的昵称push进user,打印user.length即可知道已成功连接用户的人数。
等一等。
在用户连接的时输入昵称登录,我们应该检测一下用户的昵称是否已存在,避免昵称相同的情况发生,在服务器监听一个登录事件来判断该情况,由于一切都发生在用户连接之后,所以触发事件应该写在connection事件的回调函数中。
{
// 渲染在线人员
io.emit('disUser',usersInfo);
// 登录,检测用户名
socket.on('login',(user)=> {
if(users.indexOf(user.name) > -1) { // 昵称是否存在
socket.emit('loginError'); // 触发客户端的登录失败事件
} else {
users.push(user.name); //储存用户的昵称
usersInfo.push(user); // 储存用户的昵称和头像
socket.emit('loginSuc'); // 触发客户端的登录成功事件
socket.nickname = user.name;
io.emit('system',{ // 向所有用户广播该用户进入房间
name: user.name,status: '进入'
});
io.emit('disUser',usersInfo); // 渲染右侧在线人员信息
console.log(users.length + ' user connect.'); // 打印连接人数
}
});
system和disUser事件先不管,之后再说 区分io.emit(foo)、socket.emit(foo)、socket.broadcast.emit(foo)
接下来是客户端代码chat-client.js
// 点击输入昵称
$('#nameBtn').click(()=> {
var imgN = Math.floor(Math.random()*4)+1; // 随机分配头像
if($('#name').val().trim()!=='')
socket.emit('login',{ // 触发服务器端登录事件
name: $('#name').val(),img: 'image/user' + imgN + '.jpg'
});
return false;
});
// 登录成功,隐藏登录层
socket.on('loginSuc',()=> {
$('.name').hide();
})
socket.on('loginError',()=> {
alert('用户名已存在,请重新输入!');
$('#name').val('');
});
});
倘若登录成功,会看到如下页面:
登录检测完成。
3、系统自动提示用户状态(进入/离开)
该功能是为了实现上图所示的系统提示“XXX进入聊天室”,在登录成功时触发system事件,向所有用户广播信息,注意此时用的是io.emit而不是socket.emit,客户端代码如下
{
var data = new Date().toTimeString().substr(0,8);
$('#messages').append(`
${data}
${user.name} ${user.status}了聊天室
`);
// 滚动条总是在最底部
$('#messages').scrollTop($('#messages')[0].scrollHeight);
});
4、显示在线用户
客户端监听一个显示在线用户的事件disUser,在以下三个时间段服务器端就触发一次该事件重新渲染一次
- 程序开始启动时
- 每当用户进入房间
- 每当用户离开房间
{
displayUser(usersInfo);
});
// 显示在线人员
function displayUser(users) {
$('#users').text(''); // 每次都要重新渲染
if(!users.length) {
$('.contacts p').show();
} else {
$('.contacts p').hide();
}
$('#num').text(users.length);
for(var i = 0; i < users.length; i++) {
var $html = `
${users[i].name}
`;
$('#users').append($html);
}
}
5、支持发送和接收消息
用户发送消息时触发服务器端的sendMsg事件,并将消息内容作为参数,服务器端监听到sendMsg事件之后向其他所有用户广播该消息,用的socket.broadcast.emit(foo)
{
var img = '';
for(var i = 0; i < usersInfo.length; i++) {
if(usersInfo[i].name == socket.nickname) {
img = usersInfo[i].img;
}
}
socket.broadcast.emit('receiveMsg',{ // 向除了发送者之外的其他用户广播
name: socket.nickname,img: img,msg: data.msg,color: data.color,side: 'left'
});
socket.emit('receiveMsg',{ // 向发送者发送消息,为什么分开发送?因为css样式不同
name: socket.nickname,side: 'right'
});
});
服务器端接受到来自用户的消息后会触发客户端的receiveMsg事件,并将用户发送的消息作为参数传递,该事件会向聊天面板添加聊天内容,以下为chat-client.js代码
{
if(ev.which == 13) {
sendMsg();
}
});
// 接收消息
socket.on('receiveMsg',(obj)=> { // 将接收到的消息渲染到面板上
$('#messages').append(`
${obj.name}
${obj.msg}
`);
// 滚动条总是在最底部
$('#messages').scrollTop($('#messages')[0].scrollHeight);
});
// 发送消息
function sendMsg() {
if($('#m').val() == '') { // 输入消息为空
alert('请输入内容!');
return false;
}
socket.emit('sendMsg',{
msg: $('#m').val()
});
$('#m').val('');
return false;
}
发送表情其实很简单,将表情图片放在li中,当用户点击li时就将表情的src中的序号解析出来,用[emoji+表情序号]的格式存放在聊天框里,点击发送后再解析为src。就是一个解析加还原的过程,这一过程中我们的服务器代码不变,需要改变的是客户端监听的receiveMsg事件。
// 显示表情选择面板
$('#smile').click(()=> {
$('.selectBox').css('display',"block");
});
$('#smile').dblclick((ev)=> {
$('.selectBox').css('display',"none");
});
$('#m').click(()=> {
$('.selectBox').css('display',"none");
});
// 用户点击发送表情
$('.emoji li img').click((ev)=> {
ev = ev || window.event;
var src = ev.target.src;
var emoji = src.replace(/\D*/g,'').substr(6,8); // 提取序号
var old = $('#m').val(); // 用户输入的其他内容
$('#m').val(old+'[emoji'+emoji+']');
$('.selectBox').css('display',"none");
});
// 接收消息
socket.on('receiveMsg',(obj)=> {
// 提取文字中的表情加以渲染
var msg = obj.msg;
var content = '';
while(msg.indexOf('[') > -1) { // 其实更建议用正则将[]中的内容提取出来
var start = msg.indexOf('[');
var end = msg.indexOf(']');
content += '<span>'+msg.substr(0,start)+'</span>';
content += '<img src="image/emoji/emoji%20('+msg.substr(start+6,end-start-6)+').png">';
msg = msg.substr(end+1,msg.length);
}
content += ''+msg+'';
$('#messages').append(`
${obj.name}
`);
// 滚动条总是在最底部
$('#messages').scrollTop($('#messages')[0].scrollHeight);
});