溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

使用node和express怎么實(shí)現(xiàn)一個聊天室

發(fā)布時間:2021-04-09 16:10:41 來源:億速云 閱讀:188 作者:Leah 欄目:web開發(fā)

本篇文章為大家展示了使用node和express怎么實(shí)現(xiàn)一個聊天室,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。

項(xiàng)目結(jié)構(gòu)

使用node和express怎么實(shí)現(xiàn)一個聊天室

1、將聊天室部署到服務(wù)器

先用node搭建一個服務(wù)器,部署在localhost:3000端口,先嘗試向?yàn)g覽器發(fā)送一個“hello world”,新建server.js文件。

var app = require('express')(); // 引入express模塊
var http = require('http').Server(app);

app.get('/', function(req, res){ // 路由為localhost:3000時向客戶端響應(yīng)“hello world”
 res.send('<h2>Hello world</h2>'); // 發(fā)送數(shù)據(jù)
});

http.listen(3000, function(){ // 監(jiān)聽3000端口
 console.log('listening on *:3000'); 
});

打開瀏覽器輸入網(wǎng)址:localhost:3000是這樣的

使用node和express怎么實(shí)現(xiàn)一個聊天室

一個node服務(wù)器搭建成功。

接下來用express向?yàn)g覽器返回一個html頁面

#安裝express模塊
npm install --save express

將server.js的代碼改一下:

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

// 路由為/默認(rèn)www靜態(tài)文件夾
app.use('/', express.static(__dirname + '/www'));

express.static(__dirname + '/www');是將www文件夾托管為靜態(tài)資源,意味著這個文件夾里的文件(html、css、js)彼此可以用相對路徑。在www文件夾中添加index.html文件以及相應(yīng)的css(相應(yīng)css代碼就不貼了,詳情見源碼),如下,該頁面用了font-awesome小圖標(biāo)

<!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" rel="external nofollow" >
 <link rel="stylesheet" href="style/font-awesome-4.7.0/css/font-awesome.min.css" rel="external nofollow" >
 </head>
 <body>
 <div class="all">
  <div class="name">
  <!-- <h3>請輸入你的昵稱</h3> -->
  <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">發(fā)送</button>
     <button class="btn" id="clear">關(guān)閉</button>
    </form>
   </div>
   <div class="contacts">
   <h2>在線人員(<span id="num">0</span>)</h2>
   <ul id="users"></ul>
   <p>當(dāng)前無人在線喲~</p>
   </div>
  </div>
  </div> 
 </div> 
 </body>
</html>

打開localhost:3000,會看到如下:

使用node和express怎么實(shí)現(xiàn)一個聊天室

聊天室成功部署到服務(wù)器。

2、檢測登錄

在客戶端和服務(wù)器之間傳送消息需要用到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){ // 用戶連接時觸發(fā)
 console.log('a user connected');
});

http.listen(3000, function(){
 console.log('listening on *:3000');
});

當(dāng)打開localhost:3000的時候會觸發(fā)服務(wù)器端io的connection事件,會在服務(wù)器打印“a user connected”,但是我們想統(tǒng)計(jì)一下連接該服務(wù)器的用戶人數(shù),如果有用戶連接就打印“n users connected”,n為用戶人數(shù),怎么辦呢?

在server.js設(shè)置一個全局?jǐn)?shù)組為user,每當(dāng)一個用戶連接成功就在連接事件中將用戶的昵稱push進(jìn)user,打印user.length即可知道已成功連接用戶的人數(shù)。

等一等。

在用戶連接的時輸入昵稱登錄,我們應(yīng)該檢測一下用戶的昵稱是否已存在,避免昵稱相同的情況發(fā)生,在服務(wù)器監(jiān)聽一個登錄事件來判斷該情況,由于一切都發(fā)生在用戶連接之后,所以觸發(fā)事件應(yīng)該寫在connection事件的回調(diào)函數(shù)中。

io.on('connection', (socket)=> {
 // 渲染在線人員
 io.emit('disUser', usersInfo);

 // 登錄,檢測用戶名
 socket.on('login', (user)=> {
  if(users.indexOf(user.name) > -1) { // 昵稱是否存在
   socket.emit('loginError'); // 觸發(fā)客戶端的登錄失敗事件
  } else {
   users.push(user.name); //儲存用戶的昵稱
   usersInfo.push(user); // 儲存用戶的昵稱和頭像
   socket.emit('loginSuc'); // 觸發(fā)客戶端的登錄成功事件
   socket.nickname = user.name;
   io.emit('system', { // 向所有用戶廣播該用戶進(jìn)入房間
    name: user.name,
    status: '進(jìn)入'
   });
   io.emit('disUser', usersInfo); // 渲染右側(cè)在線人員信息
   console.log(users.length + ' user connect.'); // 打印連接人數(shù)
  }
 });

system和disUser事件先不管,之后再說 區(qū)分io.emit(foo)、socket.emit(foo)、socket.broadcast.emit(foo)

io.emit(foo); //會觸發(fā)所有客戶端用戶的foo事件
socket.emit(foo); //只觸發(fā)當(dāng)前客戶端用戶的foo事件
socket.broadcast.emit(foo); //觸發(fā)除了當(dāng)前客戶端用戶的其他用戶的foo事件

接下來是客戶端代碼chat-client.js

$(function() {
  // io-client
  // 連接成功會觸發(fā)服務(wù)器端的connection事件
  var socket = io();

  // 點(diǎn)擊輸入昵稱
  $('#nameBtn').click(()=> { 
   var imgN = Math.floor(Math.random()*4)+1; // 隨機(jī)分配頭像
   if($('#name').val().trim()!=='')
     socket.emit('login', { // 觸發(fā)服務(wù)器端登錄事件
      name: $('#name').val(),
      img: 'image/user' + imgN + '.jpg'
     }); 
   return false; 
  });
  // 登錄成功,隱藏登錄層
  socket.on('loginSuc', ()=> { 
   $('.name').hide(); 
  })
  socket.on('loginError', ()=> {
   alert('用戶名已存在,請重新輸入!');
   $('#name').val('');
  }); 
});

倘若登錄成功,會看到如下頁面:

使用node和express怎么實(shí)現(xiàn)一個聊天室

登錄檢測完成。

3、系統(tǒng)自動提示用戶狀態(tài)(進(jìn)入/離開)

該功能是為了實(shí)現(xiàn)上圖所示的系統(tǒng)提示“XXX進(jìn)入聊天室”,在登錄成功時觸發(fā)system事件,向所有用戶廣播信息,注意此時用的是io.emit而不是socket.emit,客戶端代碼如下

// 系統(tǒng)提示消息
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、顯示在線用戶

客戶端監(jiān)聽一個顯示在線用戶的事件disUser,在以下三個時間段服務(wù)器端就觸發(fā)一次該事件重新渲染一次

  1. 程序開始啟動時

  2. 每當(dāng)用戶進(jìn)入房間

  3. 每當(dāng)用戶離開房間

// 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、支持發(fā)送和接收消息

用戶發(fā)送消息時觸發(fā)服務(wù)器端的sendMsg事件,并將消息內(nèi)容作為參數(shù),服務(wù)器端監(jiān)聽到sendMsg事件之后向其他所有用戶廣播該消息,用的socket.broadcast.emit(foo)

 // server.js
  // 發(fā)送消息事件
  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', { // 向除了發(fā)送者之外的其他用戶廣播
      name: socket.nickname,
      img: img,
      msg: data.msg,
      color: data.color,
      side: 'left'
    });
    socket.emit('receiveMsg', { // 向發(fā)送者發(fā)送消息,為什么分開發(fā)送?因?yàn)閏ss樣式不同
      name: socket.nickname,
      img: img,
      msg: data.msg,
      color: data.color,
      side: 'right'
    });
  });

服務(wù)器端接受到來自用戶的消息后會觸發(fā)客戶端的receiveMsg事件,并將用戶發(fā)送的消息作為參數(shù)傳遞,該事件會向聊天面板添加聊天內(nèi)容,以下為chat-client.js代碼

// 點(diǎn)擊按鈕或回車鍵發(fā)送消息
  $('#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);
  });


  // 發(fā)送消息
  function sendMsg() { 
   if($('#m').val() == '') { // 輸入消息為空
    alert('請輸入內(nèi)容!');
    return false;
   }
   socket.emit('sendMsg', {
    msg: $('#m').val()
   });
   $('#m').val(''); 
   return false; 
  }

6、自定義字體顏色

得益于html5的input新特性,可以通過type為color的input調(diào)用系統(tǒng)調(diào)色板

<!-- $('#color').val();為選中顏色,格式為#FFCCBB -->
<input type='color' id='color'>

客戶端根據(jù)用戶選擇的顏色渲染內(nèi)容樣式,代碼很容易看懂,這里就不贅述了。

7、支持發(fā)送表情

發(fā)送表情其實(shí)很簡單,將表情圖片放在li中,當(dāng)用戶點(diǎn)擊li時就將表情的src中的序號解析出來,用[emoji+表情序號]的格式存放在聊天框里,點(diǎn)擊發(fā)送后再解析為src。就是一個解析加還原的過程,這一過程中我們的服務(wù)器代碼不變,需要改變的是客戶端監(jiān)聽的receiveMsg事件。

// chat-client.js

  // 顯示表情選擇面板
  $('#smile').click(()=> {
   $('.selectBox').css('display', "block");
  });
  $('#smile').dblclick((ev)=> { 
   $('.selectBox').css('display', "none");
  }); 
  $('#m').click(()=> {
   $('.selectBox').css('display', "none");
  });

  // 用戶點(diǎn)擊發(fā)送表情
  $('.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(); // 用戶輸入的其他內(nèi)容
    $('#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) { // 其實(shí)更建議用正則將[]中的內(nèi)容提取出來
    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 >${content}</p>
     </div>
    </li>
   `);
   // 滾動條總是在最底部
   $('#messages').scrollTop($('#messages')[0].scrollHeight);
  });

可以成功發(fā)送表情了。

使用node和express怎么實(shí)現(xiàn)一個聊天室

8、支持發(fā)送圖片

首先是圖片按鈕樣式,發(fā)送圖片的按鈕是type為file的input。這里有一個改變樣式的小技巧,那就是將input的透明度設(shè)為0,z-index為5,將你想要得樣式放在div中,z-index設(shè)為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;
}

完美

使用node和express怎么實(shí)現(xiàn)一個聊天室

接下來是點(diǎn)擊按鈕發(fā)送圖片,我們用了fileReader對象,這里有一篇不錯的文章講解了fileReader,fileReader是一個對象,可以將我們選中的文件已64位輸出然后將結(jié)果存放在reader.result中,我們選中圖片之后,reader.result就存放的是圖片的src

// chat-client.js

  // 用戶發(fā)送圖片
  $('#file').change(function() {
   var file = this.files[0]; // 上傳單張圖片
   var reader = new FileReader();

   //文件讀取出錯的時候觸發(fā)
   reader.onerror = function(){
     console.log('讀取文件失敗,請重試!'); 
   };
   // 讀取成功后
   reader.onload = function() {
    var src = reader.result; // 讀取結(jié)果
    var img = '<img class="sendImg" src="'+src+'">';
    socket.emit('sendMsg', { // 發(fā)送
     msg: img,
     color: color,
     type: 'img' // 發(fā)送類型為img
    }); 
   };
   reader.readAsDataURL(file); // 讀取為64位
  });

由于發(fā)送的是圖片,所以對頁面布局難免有影響,為了頁面美觀客戶端在接收其他用戶發(fā)送的消息的時候會先判斷發(fā)送的是文本還是圖片,根據(jù)不同的結(jié)果展示不同布局。判斷的方法是在客戶發(fā)送消息的時候傳入一個type,根據(jù)type的值來確實(shí)發(fā)送內(nèi)容的類型。所以上面發(fā)送圖片代碼中觸發(fā)了sendMsg事件,傳入?yún)?shù)多了一個type屬性。

響應(yīng)的,我們應(yīng)該在chat-client.js中修改receiveMsg事件監(jiān)聽函數(shù),改為根據(jù)傳入type做不同操作

chat-client.js
  // 接收消息
  socket.on('receiveMsg', (obj)=> { 
   // 發(fā)送為圖片
   if(obj.type == 'img') {
    $('#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);
    return;
   }

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

上述內(nèi)容就是使用node和express怎么實(shí)現(xiàn)一個聊天室,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI