Single‧9 |
Node.js 與 Socket.io – 即時聊天室實作(二) Posted: 05 Jan 2018 12:50 AM PST 前一篇文章,我就假設你看過了也做完了,我們有了一個「超級無敵簡單」的聊天室,但那實在是太簡單了、太陽春了。 身為一個聊天室,它沒有前人的紀錄,也沒有每人專屬的名稱,喔對,還不會紀錄名稱!這真是太令人失望的聊天室,實在是太失望,完全就只是個拿來作為教學用的超級無敵簡單版本。作者到底做了什麼鬼東西啊! 喔等等,作者好像就是我。 好吧,為了盡棄前嫌,我決定來改良改良,增加一些新功能。
前情提要請轉到Node.js 與 Socket.io – 即時聊天室實作收看。 名稱第一個打算加入的是名稱,為什麼呢?因為比較簡單。 程式views/insdex.html // 加入 Cookies function setCookie(cname, cvalue, exdays) { var d = new Date(); d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); var expires = "expires="+d.toUTCString(); document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; } function getCookie(cname) { var name = cname + "="; var ca = document.cookie.split(';'); for(var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') { c = c.substring(1); } if (c.indexOf(name) == 0) { return c.substring(name.length, c.length); } } return ""; } // ... var content = document.getElementById("content"); // 加入下面這些 var nameInputBox = document.getElementById("name"); var name = getCookie("name"); if (name) { nameInputBox.value = name; } // .... if (ok) socket.emit("send", formData); // 這行替換成下面的程式片段 if (ok) { socket.emit("send", formData); setCookie("name", nameInputBox.value); } 這部分我們新增了 Cookies 的存取,用來實現我們的名稱記錄的功能。 這功能會在第一次送出成功時,將名稱存入 Cookies 之中,之後用同一個瀏覽器進入聊天室,你的名稱將會維持與先前所輸入的名字一樣的名稱。 聊天記錄程式records.js const {EventEmitter} = require("events"); let instance; let data = []; let MAX = 50; class Records extends EventEmitter { constructor () { super(); } push (msg) { data.push(msg); if (data.length > MAX) { data.splice(0, 1); } this.emit("new_message", msg); } get () { return data; } setMax (max) { MAX = max; } getMax () { return MAX; } } module.exports = (function () { if (!instance) { instance = new Records(); } return instance; })(); index.js const server = require('http').Server(app); const io = require('socket.io')(server); const records = require('./records.js'); // 新增這行 // ... io.on('connection', (socket) => { // 有連線發生時增加人數 onlineCount++; // 發送人數給網頁 io.emit("online", onlineCount); socket.emit("maxRecord", records.getMax()); // 新增記錄最大值,用來讓前端網頁知道要放多少筆 socket.emit("chatRecord", records.get()); // 新增發送紀錄 socket.on("send", (msg) => { // 如果 msg 內容鍵值小於 2 等於是訊息傳送不完全 // 因此我們直接 return ,終止函式執行。 if (Object.keys(msg).length < 2) return; records.push(msg); //io.emit("msg", msg); // 這行刪除改由 Records 事件接手 }); socket.on('disconnect', () => { // 有人離線了,扣人 onlineCount = (onlineCount < 0) ? 0 : onlineCount-=1; io.emit("online", onlineCount); }); }); // 新增 Records 的事件監聽器 records.on("new_message", (msg) => { // 廣播訊息到聊天室 io.emit("msg", msg); }); views/index.html document.addEventListener("DOMContentLoaded", () => { var max_record; // 新增 // ... // 加入新的事件監聽器 socket.on("chatRecord", function (msgs) { for (var i=0; i < msgs.length; i++) { (function () { addMsgToBox(msgs[i]); })(); } }); socket.on("maxRecord", function (amount) { max_record = amount; }); // 修改 msg 事件監聽器 socket.on("msg", addMsgToBox); // 新增兩個 function // 新增訊息到方框中 function addMsgToBox (d) { var msgBox = document.createElement("div") msgBox.className = "msg"; var nameBox = document.createElement("span"); nameBox.className = "name"; var name = document.createTextNode(d.name); var msg = document.createTextNode(d.msg); nameBox.appendChild(name); msgBox.appendChild(nameBox); msgBox.appendChild(msg); content.appendChild(msgBox); if (content.children.length > max_record) { rmMsgFromBox(); } } // 移除多餘的訊息 function rmMsgFromBox () { var childs = content.children; childs[0].remove(); } 在這邊我們新增了一個物件 而前端的調整主要是在第一次進入畫面,收到紀錄資料時的顯示方式,以及聊天筆數太多時要除掉舊的記錄這樣。 好了,啟動伺服器,連到 完整程式index.js const express = require('express'); const app = express(); const server = require('http').Server(app); const io = require('socket.io')(server); const records = require('./records.js'); const port = process.env.PORT || 3000; // 加入線上人數計數 let onlineCount = 0; app.get('/', (req, res) => { res.sendFile( __dirname + '/views/index.html'); }); io.on('connection', (socket) => { // 有連線發生時增加人數 onlineCount++; // 發送人數給網頁 io.emit("online", onlineCount); // 發送紀錄最大值 socket.emit("maxRecord", records.getMax()); // 發送紀錄 socket.emit("chatRecord", records.get()); socket.on("greet", () => { socket.emit("greet", onlineCount); }); socket.on("send", (msg) => { // 如果 msg 內容鍵值小於 2 等於是訊息傳送不完全 // 因此我們直接 return ,終止函式執行。 if (Object.keys(msg).length < 2) return; records.push(msg); }); socket.on('disconnect', () => { // 有人離線了,扣人 onlineCount = (onlineCount < 0) ? 0 : onlineCount-=1; io.emit("online", onlineCount); }); }); records.on("new_message", (msg) => { // 廣播訊息到聊天室 io.emit("msg", msg); }); server.listen(port, () => { console.log("Server Started. http://localhost:" + port); }); records.js const {EventEmitter} = require("events"); let instance; let data = []; let MAX = 50; class Records extends EventEmitter { constructor () { super(); } push (msg) { data.push(msg); if (data.length > MAX) { data.splice(0, 1); } this.emit("new_message", msg); } get () { return data; } setMax (max) { MAX = max; } getMax () { return MAX; } } module.exports = (function () { if (!instance) { instance = new Records(); } return instance; })(); views/index.html <!DOCTYPE html> <html lang="zh-tw"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My Chatroom</title> <script src="/socket.io/socket.io.js"></script> <script> var socket = io(); </script> <style> html, body { padding: 0; margin: 0; } #container { top: 50px; width: 500px; margin: 0 auto; display: block; position: relative; } #status-box { text-align: right; font-size: .6em; } #content { width: 100%; height: 350px; border: 1px solid darkolivegreen; border-radius: 5px; overflow: auto; } #send-box { width: 100%; text-align: center; } #send-box input { display: inline-block; } #send-box input.error { border: 1px solid red; } input[name="name"] { width: 15%; } input[name="msg"] { width: 70%; } input[type="button"] { width: 10%; } .msg { width: 73%; display: inline-block; padding: 5px 0 5px 10px; } .msg > span { width: 25%; display: inline-block; } .msg > span::before { color: darkred; content: " { "; } .msg > span::after { color: darkred; content: " } "; } </style> </head> <body> <div id="container"> <div id="status-box">Server: <span id="status">-</span> / <span id="online">0</span> online.</div> <div id="content"> </div> <div id="send-box"> <form id="send-form"> <input type="text" name="name" id="name" placeholder="暱稱"> <input type="text" name="msg" id="msg" placeholder="說點什麼?"> <input type="submit" value="送出"> </form> </div> </div> <script> document.addEventListener("DOMContentLoaded", () => { var max_record; var status = document.getElementById("status"); var online = document.getElementById("online"); var sendForm = document.getElementById("send-form"); var content = document.getElementById("content"); var nameInputBox = document.getElementById("name"); var name = getCookie("name"); if (name) { nameInputBox.value = name; } socket.on("connect", function () { status.innerText = "Connected."; }); socket.on("disconnect", function () { status.innerText = "Disconnected."; }); socket.on("online", function (amount) { online.innerText = amount; }); socket.on("maxRecord", function (amount) { max_record = amount; }); socket.on("chatRecord", function (msgs) { for (var i=0; i < msgs.length; i++) { (function () { addMsgToBox(msgs[i]); })(); } }); socket.on("msg", addMsgToBox); sendForm.addEventListener("submit", function (e) { e.preventDefault(); var ok = true; var formData = { time: new Date().toUTCString() }; var formChild = sendForm.children; for (var i=0; i< sendForm.childElementCount; i++) { var child = formChild[i]; if (child.name !== "") { var val = child.value; if (val === "" || !val) { ok = false; child.classList.add("error"); } else { child.classList.remove("error"); formData[child.name] = val; } } } if (ok) { socket.emit("send", formData); setCookie("name", nameInputBox.value); } }); function addMsgToBox (d) { var msgBox = document.createElement("div") msgBox.className = "msg"; var nameBox = document.createElement("span"); nameBox.className = "name"; var name = document.createTextNode(d.name); var msg = document.createTextNode(d.msg); nameBox.appendChild(name); msgBox.appendChild(nameBox); msgBox.appendChild(msg); content.appendChild(msgBox); if (content.children.length > max_record) { rmMsgFromBox(); } } function rmMsgFromBox () { var childs = content.children; childs[0].remove(); } function setCookie(cname, cvalue, exdays) { var d = new Date(); d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); var expires = "expires="+d.toUTCString(); document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; } function getCookie(cname) { var name = cname + "="; var ca = document.cookie.split(';'); for(var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') { c = c.substring(1); } if (c.indexOf(name) == 0) { return c.substring(name.length, c.length); } } return ""; } }); </script> </body> </html> Have Fun The post Node.js 與 Socket.io – 即時聊天室實作(二) appeared first on Single.9. |
You are subscribed to email updates from Single.9. To stop receiving these emails, you may unsubscribe now. |
Email delivery powered by Google |
Google, 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States |