效果图:
1. 创建一个package.json,搭建本项目初始依赖
npm init -y
// 填写package.json的对应内容
{
"name": "websocket-express-webchat",
"version": "1.0.1",
"description": "基于websocket的一个简单的聊天室",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.14.1",
"socket.io": "^1.7.2"
}
}
// 填写完成后使用npm i 或 yarn add 下载项目依赖
2. 新建角色选择页面,可以选择不同的角色
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ws聊天室</title>
<link href="/public/base.css" />
<style>
.title {
text-align: center;
margin-top: 100px;
}
.userList {
display: flex;
justify-content: center;
list-style: none;
flex-wrap: wrap;
}
.userList li {
flex: 1;
float: left;
margin: 20px;
background-color: #5bd1d7;
text-align: center;
border-radius: 20px;
line-height: 200px;
cursor: pointer;
}
.userList li a {
display: block;
width: 100%;
height: 100%;
color: #515bd4;
text-decoration: none;
}
</style>
</head>
<body>
<h1 class="title">请选择您的角色</h1>
<ul class="userList">
<li><a href="/user1">user1</a></li>
<li><a href="/user2">user2</a></li>
<li><a href="/user3">user3</a></li>
<li><a href="/user4">user4</a></li>
</ul>
</body>
</html>
3. 新建public文件夹,存储静态资源部分
websocket-express-webchat
│
├── node-modules/
│
├── public/
│ ├── img/
│ │ └── user1.jpg
│ │ └── user2.jpg
│ │ └── user3.jpg
│ │ └── user4.jpg
│ └── base.css
│ └── user.css
│ └── user1.html
│ └── user2.html
│ └── user3.html
│ └── user4.html
│
└── app.js
└── index.html
└── app.js
4. 搭建各个用户界面
// user1.html,user2.html,user3.html,user4.html通用,只需要根据内部注释实时修改user的值为1,2,3,4即可
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./base.css" />
<link rel="stylesheet" href="./user.css" />
<title>Document</title>
</head>
<body>
<div class="chatBody"></div>
<div class="chatBox">
<input type="text" name="" id="" cols="30" rows="10" placeholder="请输入内容" class="textarea" />
<div class="btn">发送</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script>
const btn = document.querySelector('.btn');
const textarea = document.querySelector('.textarea');
const chatBody = document.querySelector('.chatBody');
const imgList = ['./img/user1.jpg', './img/user2.jpg', './img/user3.jpg', './img/user4.jpg'];
// 通过io第三方库与websocket建立连接
const ws = io('http://localhost:3000');
// 自定义事件,当websocket调用news方法时这里的回调函数执行,并携带对应的参数
ws.on('news', data => {
// 这里如果是user1,则判断data.user===1,反之为user2.则判断data.user===2......
if (data.user === 1) return;
chatBody.innerHTML += generateStr('left', imgList[data.user - 1], data.content, data.time);
});
ws.onopen = function () {
console.log(ws.readyState, '连接成功');
};
ws.onclose = function () {
console.log('取消连接');
};
ws.onerror = function (err) {
console.log('连接错误', err);
};
ws.onmessage = function (msg) {
console.log('接收消息msg', msg);
};
let str = '';
// textarea输入内容时触发
textarea.addEventListener('input', e => {
str = textarea.value;
});
// 发布消息函数
const publicMessage = () => {
if (str.trim().length !== 0) {
chatBody.innerHTML += generateStr('right', imgList[0], str, new Date());
// 调用websocket自定义的sendMsg方法,并传递对应的数据
ws.emit('sendMsg', {
content: str,
// 这里表示当前用户的id,会在发送消息时携带过去,用于标识当前的用户,当前如果为user1.html,这里的值就为user: 1,如果为user2.html,这里的值就为user: 2
user: 1,
time: new Date(),
});
}
str = '';
textarea.value = '';
chatBody.scrollTo({
top: chatBody.scrollHeight,
behavior: 'smooth',
});
};
textarea.addEventListener('keydown', e => {
if (e.keyCode === 13) {
// 发布
publicMessage();
}
});
// 生成html字符串,用于填充进chatBody中
const generateStr = (type, img, value, time) => {
return type === 'right'
? `<div class="right">
<div class="text">
<p>${value}</p>
<p>${format(time, 'yyyy-MM-dd hh:mm:ss')}</p>
</div>
<img src="${img}" alt="" />
</div>`
: `<div class="left">
<img src="${img}" alt="" />
<div class="text">
<p>${value}</p>
<p>${format(time, 'yyyy-MM-dd hh:mm:ss')}</p>
</div>
</div>`;
};
// 点击发布时触发
btn.addEventListener('click', publicMessage);
// 日期格式化函数
function format(date, fmt) {
if (date instanceof Date) return '';
let time = new Date(date);
const o = {
'M+': time.getMonth() + 1,
'd+': time.getDate(),
'h+': time.getHours(),
'm+': time.getMinutes(),
's+': time.getSeconds(),
'q+': Math.floor((time.getMonth() + 3) / 3),
S: time.getMilliseconds(),
};
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (time.getFullYear() + '').substr(4 - RegExp.$1.length));
}
for (let k in o) {
if (new RegExp('(' + k + ')').test(fmt)) {
fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length));
}
}
return fmt;
}
</script>
</body>
</html>
5. 新建app.js文件,使用node环境启动,负责websocket消息的接收与转发以及开发服务器的启动
// app.js
// 引入express库
var express = require('express');
// 引入socket.io库用于实现websocket通信
var socket = require('socket.io');
// 设置文件根路径
var path = __dirname + '/public'
// 创建并启动服务器
var app = express();
var server = app.listen(3000, () => {
console.log(`http://localhost:3000`);
});
// 创建websocket实例,并与服务器进行关联
var ws = new socket(server);
// 处理websocket事件
ws.on('connection', (socket) => {
// websocket收到sendMsg事件时调用这里的回调函数(senhdMsg是某个页面发送消息的事件)
socket.on('sendMsg', (data) => {
// 当页面发送消息时,就将收到的消息进行广播,所以连接词websocket的页面都可以收到
ws.sockets.emit('news', data);
})
});
// 处理静态文件请求
app.use(express.static(path));
// 处理路由请求
app.get('/', (req, res) => {
// 返回 index.html 页面
res.sendFile(__dirname + '/index.html');
});
// 处理 user1 页面请求
app.get('/user1', (req, res) => {
// 返回 user1.html 页面
res.sendFile(path + '/user1.html');
});
// 处理 user2 页面请求
app.get('/user2', (req, res) => {
// 返回 user2.html 页面
res.sendFile(path + '/user2.html');
});
// 处理 user3 页面请求
app.get('/user3', (req, res) => {
// 返回 user3.html 页面
res.sendFile(path + '/user3.html');
});
// 处理 user4 页面请求
app.get('/user4', (req, res) => {
// 返回 user4.html 页面
res.sendFile(path + '/user4.html');
});
// 处理第三方库请求
// 使用静态文件中间件,设置第三方库路径
app.use(express.static('node_modules'));
6. 启动app.js
// 在当前项目的目录下终端启动输入命令
node app.js
// 根据终端提示的url信息,访问页面即可实现聊天室效果