零基础实现node+express个性化聊天室

本篇文章使用node+express+jquery写一个个性化聊天室,一起来get一下~(源码地址见文章末尾)

效果图

项目结构

实现功能

  1. 登录检测
  2. 系统自动提示用户状态(进入/离开)
  3. 显示在线用户
  4. 支持发送和接收消息
  5. 自定义字体颜色
  6. 支持发送表情
  7. 支持发送图片

下面将一一讲解如何实现

前期准备

node及npm环境expresssocket.io

具体实现

1、将聊天室部署到服务器

先用node搭建一个服务器,部署在localhost:3000端口,先尝试向浏览器发送一个“hello world”,新建server.js文件。

 1 var app = require(‘express‘)();  // 引入express模块
 2 var http = require(‘http‘).Server(app);
 3
 4 app.get(‘/‘, function(req, res){  // 路由为localhost:3000时向客户端响应“hello world”
 5   res.send(‘<h1>Hello world</h1>‘);  // 发送数据
 6 });
 7
 8 http.listen(3000, function(){  // 监听3000端口
 9   console.log(‘listening on *:3000‘);
10 }); 

打开浏览器输入网址:localhost:3000是这样的

一个node服务器搭建成功。

接下来用express向浏览器返回一个html页面

1 #安装express模块
2 npm install --save express

将server.js的代码改一下:

var express = require(‘express‘);
var app = express();
var http = require(‘http‘).Server(app); 

// 路由为/默认www静态文件夹
app.use(‘/‘, express.static(__dirname + ‘/www‘));
  • express.static(__dirname + ‘/www‘);是将www文件夹托管为静态资源,意味着这个文件夹里的文件(html、css、js)彼此可以用相对路径。

在www文件夹中添加index.html文件以及相应的css(相应css代码就不贴了,详情见源码),如下,该页面用了font-awesome小图标

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>chat</title>
    <link rel="stylesheet" href="style/index.css">
    <link rel="stylesheet" href="style/font-awesome-4.7.0/css/font-awesome.min.css">
  </head>
  <body>
    <div class="all">
      <div class="name">
        <!-- <h2>请输入你的昵称</h2> -->
        <input type="text" id="name" placeholder="请输入昵称..." autocomplete="off">
        <button id="nameBtn">确  定</button>
      </div>
      <div class="main">
        <div class="header">
          <img src="image/logo.jpg">
          happy聊天室
        </div>
        <div id="container">
          <div class="conversation">
              <ul id="messages"></ul>
              <form action="">
                  <div class="edit">
                    <input type="color" id="color" value="#000000">
                    <i title="双击取消选择" class="fa fa-smile-o" id="smile">
                    </i><i title="双击取消选择" class="fa fa-picture-o" id="img"></i>
                    <div class="selectBox">
                      <div class="smile">
                      </div>
                      <div class="img">
                      </div>
                    </div>
                  </div>
                  <!-- autocomplete禁用自动完成功能 -->
                  <textarea id="m"></textarea>
                  <button class="btn rBtn" id="sub">发送</button>
                  <button class="btn" id="clear">关闭</button>
              </form>
          </div>
          <div class="contacts">
            <h1>在线人员(<span id="num">0</span>)</h1>
            <ul id="users"></ul>
            <p>当前无人在线哟~</p>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

打开localhost:3000,会看到如下:

聊天室成功部署到服务器。

2、检测登录

在客户端和服务器之间传送消息需要用到socket.io

#安装socket.io模块
npm install --save socket.io

将server.js改动如下:

var app = require(‘express‘)();
var http = require(‘http‘).Server(app);
var io = require(‘socket.io‘)(http);

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.on(‘connection‘, (socket)=> {
    // 渲染在线人员
    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)
/*
   io.emit(foo); //会触发所有客户端用户的foo事件
   socket.emit(foo); //只触发当前客户端用户的foo事件
   socket.broadcast.emit(foo); //触发除了当前客户端用户的其他用户的foo事件
*/

接下来是客户端代码chat-client.js

$(function() {
    // io-client
    // 连接成功会触发服务器端的connection事件
    var socket = io(); 

    // 点击输入昵称
    $(‘#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,客户端代码如下

// 系统提示消息
socket.on(‘system‘, (user)=> {
  var data = new Date().toTimeString().substr(0, 8);
  $(‘#messages‘).append(`<p class=‘system‘><span>${data}</span><br /><span>${user.name}  ${user.status}了聊天室<span></p>`);
  // 滚动条总是在最底部
  $(‘#messages‘).scrollTop($(‘#messages‘)[0].scrollHeight);
});

4、显示在线用户

客户端监听一个显示在线用户的事件disUser,在以下三个时间段服务器端就触发一次该事件重新渲染一次

  • 程序开始启动时
  • 每当用户进入房间
  • 每当用户离开房间
// chat-client.js
// 显示在线人员
socket.on(‘disUser‘, (usersInfo)=> {
  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 = `<li>
      <img src="${users[i].img}">
      <span>${users[i].name}</span>
    </li>`;
    $(‘#users‘).append($html);
  }
}

5、支持发送和接收消息

用户发送消息时触发服务器端的sendMsg事件,并将消息内容作为参数,服务器端监听到sendMsg事件之后向其他所有用户广播该消息,用的socket.broadcast.emit(foo)

// server.js
    // 发送消息事件
    socket.on(‘sendMsg‘, (data)=> {
        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,
            img: img,
            msg: data.msg,
            color: data.color,
            side: ‘right‘
        });
    });

服务器端接受到来自用户的消息后会触发客户端的receiveMsg事件,并将用户发送的消息作为参数传递,该事件会向聊天面板添加聊天内容,以下为chat-client.js代码

// 点击按钮或回车键发送消息
    $(‘#sub‘).click(sendMsg);
    $(‘#m‘).keyup((ev)=> {
      if(ev.which == 13) {
        sendMsg();
      }
    });

    // 接收消息
    socket.on(‘receiveMsg‘, (obj)=> { // 将接收到的消息渲染到面板上
      $(‘#messages‘).append(`
         <li class=‘${obj.side}‘>
          <img src="${obj.img}">
          <div>
            <span>${obj.name}</span>
            <p>${obj.msg}</p>
          </div>
        </li>
      `);
      // 滚动条总是在最底部
      $(‘#messages‘).scrollTop($(‘#messages‘)[0].scrollHeight);
    }); 

    // 发送消息
    function sendMsg() {
      if($(‘#m‘).val() == ‘‘) {  // 输入消息为空
        alert(‘请输入内容!‘);
        return false;
      }
      socket.emit(‘sendMsg‘, {
        msg: $(‘#m‘).val()
      });
      $(‘#m‘).val(‘‘);
      return false;
    }

6、自定义字体颜色

得益于html5的input新特性,可以通过type为color的input调用系统调色板

<!-- $(‘#color‘).val();为选中颜色,格式为#FFCCBB -->
<input type=‘color‘ id=‘color‘>  

客户端根据用户选择的颜色渲染内容样式,代码很容易看懂,这里就不赘述了。

7、支持发送表情

发送表情其实很简单,将表情图片放在li中,当用户点击li时就将表情的src中的序号解析出来,用[emoji+表情序号]的格式存放在聊天框里,点击发送后再解析为src。就是一个解析加还原的过程,这一过程中我们的服务器代码不变,需要改变的是客户端监听的receiveMsg事件。

// chat-client.js

    // 显示表情选择面板
    $(‘#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");
    });

客户端收到之后将表情序号还原为src,更改如下

// chat-client.js

    // 接收消息
    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 += ‘<span>‘+msg+‘</span>‘;

      $(‘#messages‘).append(`
        <li class=‘${obj.side}‘>
          <img src="${obj.img}">
          <div>
            <span>${obj.name}</span>
            <p style="color: ${obj.color};">${content}</p>
          </div>
        </li>
      `);
      // 滚动条总是在最底部
      $(‘#messages‘).scrollTop($(‘#messages‘)[0].scrollHeight);
    });

可以成功发送表情了。

8、支持发送图片

首先是图片按钮样式,发送图片的按钮是type为file的input。这里有一个改变样式的小技巧,那就是将input的透明度设为0,z-index为5,将你想要得样式放在div中,z-index设为1覆盖在input上。

<input type="file" id="file">
<i class="fa fa-picture-o" id="img"></i>

css:

.edit #file {
    width: 32.36px;
    height: 29px;
    opacity: 0;
    z-index: 5;
}
.edit #img {
    z-index: 0;
    margin-left: -43px;
}

完美

接下来是点击按钮发送图片,我们用了fileReader对象,这里有一篇不错的文章讲解了fileReader,fileReader是一个对象,可以将我们选中的文件已64位输出然后将结果存放在reader.result中,我们选中图片之后,reader.result就存放的是图片的src

// chat-client.js

    // 用户发送图片
    $(‘#file‘).change(function() {
      var file = this.files[0];  // 上传单张图片
      var reader = new FileReader();

      //文件读取出错的时候触发
      reader.onerror = function(){
          console.log(‘读取文件失败,请重试!‘);
      };
      // 读取成功后
      reader.onload = function() {
        var src = reader.result;  // 读取结果
        var img = ‘<img class="sendImg" src="‘+src+‘">‘;
        socket.emit(‘sendMsg‘, {  // 发送
          msg: img,
          color: color,
          type: ‘img‘  // 发送类型为img
        });
      };
      reader.readAsDataURL(file); // 读取为64位
    });

由于发送的是图片,所以对页面布局难免有影响,为了页面美观客户端在接收其他用户发送的消息的时候会先判断发送的是文本还是图片,根据不同的结果展示不同布局。判断的方法是在客户发送消息的时候传入一个type,根据type的值来确实发送内容的类型。所以上面发送图片代码中触发了sendMsg事件,传入参数多了一个type属性。

相应的,我们应该在chat-client.js中修改receiveMsg事件监听函数,改为根据传入type做不同操作

chat-client.js
    // 接收消息
    socket.on(‘receiveMsg‘, (obj)=> {
      // 发送为图片
      if(obj.type == ‘img‘) {
        $(‘#messages‘).append(`
          <li class=‘${obj.side}‘>
            <img src="${obj.img}">
            <div>
              <span>${obj.name}</span>
              <p style="padding: 0;">${obj.msg}</p>
            </div>
          </li>
        `);
        $(‘#messages‘).scrollTop($(‘#messages‘)[0].scrollHeight);
        return;
      }

      // 提取文字中的表情加以渲染
      // 下面不变
    }); 

现在我们可以发送图片了

圆满完成一个功能齐全的聊天室!

源码地址:windlany/happy-chat,本文断断续续写了两天,真是写文章比敲代码还累...其实写一个聊天室并不难,这算是node起步作品吧。有兴趣的可以fork下来根据自己需求改改,觉得不错请给我一个star。

参考链接

原文地址:https://www.cnblogs.com/wind-lanyan/p/8412657.html

时间: 2024-08-13 11:01:57

零基础实现node+express个性化聊天室的相关文章

Node实战之聊天室

Node实战之聊天室 Node如何同时处理Http和WebSocket 1.只出现在用户访问聊天程序网站时:Web浏览器->Http请求->Node服务器->Http响应->Web浏览器 2.在用户聊天时持续发生:Web浏览器->WebSocket数据发送->Node服务器->WebSocket数据接收->Web浏览器 开始搭建 1.创建程序文档结构(如下图所示) 2.指明依赖项 程序的依赖项是在package.json文件中指明的.这个文件总是被放在程序的

基于Node的Web聊天室

1 项目名称 Web聊天室(<这是NodeJs实战>第二章的一个案例,把整个开发过程记录下来) 2 项目描述 该项目是一个简单的在线聊天程序.打开聊天页面,程序自动给用户分配一个昵称,进入默认的Lobby聊天室.用户可以发送消息,也可以使用聊天命令(聊天命令以/开头)修改自己的昵称或者加入已有的聊天室(聊天室不存在时,创建新的聊天室).在加入或创建聊天室时,新聊天室的名称会出现在聊天程序顶端的水平条上,也会出现在聊天消息区域右侧的可用房间列表中.在用户换到新房间后,系统会显示信息以确认这一变化

node实现websocket聊天室

const express = require('express'); const app = express(); const ws = require('ws').Server; const wss = new ws({port: 3001}); var sockets = []; wss.on('connection', function(ws) { ws.send('连接成功'); ws.send('欢迎进入聊天室'); sockets.push(ws); ws.on('message'

夺命雷公狗---node下的一聊天室-首发

今晚感觉挺蛋疼,就用了点时间,在node下开发了个聊天室来玩玩,不过之是简单的开发了套而已,并没多做什么考虑,, 但是发现了一个好处就是用node来写聊天室代码居然少得可怜,这个不佩服node都不行,效果图如下所示: 说句实话,我都有点爱上node了,因为深深的被她的魅力感染了,嘻嘻,不做过多的介绍,代码其实也很简单: 说实话感觉node真的很强大,, 源码下载地址: 链接:http://pan.baidu.com/s/1cvEdTO 密码:wx9s

NodeJS Windows下零基础搭建一个视频聊天室1

NodeJS安装 1.前往 http://www.nodejs.org/download/  选择Windows Installer (.msi)版下载 2.点击安装node-v0.10.31-x86.msi文件(文件名根据版本号命名,安装最新版便是). 3.测试是否安装成功:打开cmd窗口,执行node -v,如果显示版本号说明安装成功.如果提示未找到node命令,则通过cmd窗口定位(cd命令)到安装目录,再次执行node -v 即可看到版本号了. 如果希望执行命令前不要每次定位到安装目录,

【视频】零基础学Android开发:蓝牙聊天室APP(一)

零基础学Android开发:蓝牙聊天室APP第一讲 1. Android介绍与环境搭建:史上最高效Android入门学习 1.1 Google的大小战略 1.2 物联网与云计算 1.3 智能XX设备 1.4 Android发展前景 1.5 Android企业需求与就业薪资 1.6 Android框架介绍 1.7 搭建Android开发环境 1.8 Android SDK文件夹具体解释 1.9 开发第一个App:HelloWorld 1.10 App应用程序文件夹具体解释 在线收看:http://

【视频】零基础学Android开发:蓝牙聊天室APP(三)

零基础学Android开发:蓝牙聊天室APP第三讲 3.1 ImageView.ImageButton控件详解 3.2 GridView控件详解 3.3 SimpleAdapter适配器详解 3.4 事件监听器:OnItemClickListener 3.5 输入和显示表情图像 在线收看:http://www.3g-edu.org/news/video023.htm 视频下载:http://pan.baidu.com/s/1kTmiNqf

【视频】零基础学Android开发:蓝牙聊天室APP(四)

零基础学Android开发:蓝牙聊天室APP第四讲 4.1 ListView控件的使用 4.2 BaseAdapter详解 4.3 ListView分布与滚动事件 4.4 ListView事件监听器:OnItemClickedListener 在线收看:http://www.3g-edu.org/news/video026.htm 视频下载:http://pan.baidu.com/s/1jGkjDGE

【视频】零基础学Android开发:蓝牙聊天室APP(二)

零基础学Android开发:蓝牙聊天室APP第二讲 2.1 课程内容应用场景 2.2 Android UI设计 2.3 组件布局:LinearLayout和RelativeLayout 2.4 TextView.EditText.Button控件 2.5 文本信息的隐藏和显示 2.6 输入和显示表情图像 在线收看:http://www.3g-edu.org/news/video022.htm 视频下载:http://pan.baidu.com/s/1mgHoObu