零基礎實現node+express個人化聊天室的樣本,nodeexpress
本篇文章使用node+express+jquery寫一個個人化聊天室,一起來get一下~(源碼地址見文章末尾)
項目結構
實現功能
- 登入檢測
- 系統自動提示使用者狀態(進入/離開)
- 顯示線上使用者
- 支援發送和接收訊息
- 自訂字型顏色
- 支援發送表情
- 支援發送圖片
下面將一一講解如何?
前期準備
node及npm環境、express、socket.io
具體實現
1、將聊天室部署到伺服器
先用node搭建一個伺服器,部署在localhost:3000連接埠,先嘗試向瀏覽器發送一個“hello world”,建立server.js檔案。
var app = require('express')(); // 引入express模組var http = require('http').Server(app);app.get('/', function(req, res){ // 路由為localhost:3000時向用戶端響應“hello world” res.send('<h1>Hello world</h1>'); // 發送資料});http.listen(3000, function(){ // 監聽3000連接埠 console.log('listening on *:3000'); });
開啟瀏覽器輸入網址:localhost:3000是這樣的
一個node伺服器搭建成功。
接下來用express向瀏覽器返回一個html頁面
#安裝express模組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" 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"> <!-- <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起步作品吧。
以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援幫客之家。