這篇文章主要介紹了零基礎實作node express個人化聊天室的範例,現在分享給大家,也給大家做個參考。
這篇文章使用node express jquery寫一個個人化聊天室,一起來get一下~(原始碼位址見文章最後)
#實作功能
#前期準備
##node及npm環境、express、socket.io
先用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> <p class="all"> <p class="name"> <!-- <h2>请输入你的昵称</h2> --> <input type="text" id="name" placeholder="请输入昵称..." autocomplete="off"> <button id="nameBtn">确 定</button> </p> <p class="main"> <p class="header"> <img src="image/logo.jpg"> happy聊天室 </p> <p id="container"> <p class="conversation"> <ul id="messages"></ul> <form action=""> <p 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> <p class="selectBox"> <p class="smile"> </p> <p class="img"> </p> </p> </p> <!-- autocomplete禁用自动完成功能 --> <textarea id="m"></textarea> <button class="btn rBtn" id="sub">发送</button> <button class="btn" id="clear">关闭</button> </form> </p> <p class="contacts"> <h1>在线人员(<span id="num">0</span>)</h1> <ul id="users"></ul> <p>当前无人在线哟~</p> </p> </p> </p> </p> </body> </html>
開啟localhost:3000,會看到如下:
聊天室成功部署到伺服器。
在用戶端和伺服器之間傳送訊息需要用到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(''); }); });
倘若登入成功,會看到如下頁:
#登入偵測完成。
#該功能是為了實現上圖所示的系統提示「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); });
#用戶端監聽一個顯示線上使用者的事件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); } }
用户发送消息时触发服务器端的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}"> <p> <span>${obj.name}</span> <p>${obj.msg}</p> </p> </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}"> <p> <span>${obj.name}</span> <p style="color: ${obj.color};">${content}</p> </p> </li> `); // 滚动条总是在最底部 $('#messages').scrollTop($('#messages')[0].scrollHeight); });
可以成功发送表情了。
8、支持发送图片
首先是图片按钮样式,发送图片的按钮是type为file的input。这里有一个改变样式的小技巧,那就是将input的透明度设为0,z-index为5,将你想要得样式放在p中,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}"> <p> <span>${obj.name}</span> <p style="padding: 0;">${obj.msg}</p> </p> </li> `); $('#messages').scrollTop($('#messages')[0].scrollHeight); return; } // 提取文字中的表情加以渲染 // 下面不变 });
现在我们可以发送图片了
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
在vue-cli中使用webpack模板解决项目搭建及打包路径问题
以上是如何實現node+express個人化聊天室?的詳細內容。更多資訊請關注PHP中文網其他相關文章!