基于Bootstrap5构建页面,使用Socket.io处理后台信息
学习链接
实现细节
服务端
npm init - 得到package.json
修改package.json
"main": "index.js",
-> "main": "server.js"
npm i express socket.io - 安装包,主角登场
npm i nodemon –save-dev - 测试环境安装
- 安装 - 可服务热加载 - 不需要手动重启服务,用nodemon
修改scripts - 执行命令
1 2 3
| "scripts": { "dev":"nodemon server.js" },
|
创建server.js
把所有前端文件,放入public文件!
可以开始后端代码了!
1 2 3 4 5 6 7 8 9 10 11
| const express = require('express'); // 引入资源 const socketio = require('socket.io');
const app = express(); // 创建实例
const PORT = process.env.PORT || 9527; // 定义端口
app.listen(PORT, () => { // 测试链接 console.log('This server is running on port' + PORT) })
|
输入npm run dev
可以看到
引入静态资源 - 可以看到页面了!
1 2 3
| const path = require('path');
app.use(express.static(path.join(__dirname, 'public')));
|
引入http服务,创建http对象,用socket来接收,用server来监听!
所以app - express做了什么???use。。。引入静态资源。
1 2 3 4 5 6 7 8 9 10
| const http = require('http'); // 挂载socketio
const app = express(); const server = http.createServer(app); // 手动创建http对象
const io = socketio(server); // 把http服务挂载到io
server.listen(PORT, () => { console.log('This server is running on port ' + PORT) })
|
可以访问以下路径了,socketio把客户端文件挂载到服务器上了!
http://localhost:9527/socket.io/socket.io.js
监听/发送
1 2 3 4 5
| // 监听,有没有连进来。 io.on('connection', (socket) => { io.emit('sysMessage', '欢迎加入聊天室') // 发送 })
|
在静态页内引入js - chat.html
1 2 3 4 5
| // 来自socket.io <script src="/socket.io/socket.io.js" type="text/javascript" charset="utf-8"></script>
// public下新建一个 <script src="./main.js" type="text/javascript" charset="utf-8"></script>
|
在main.js中写入
const socket = io();
就可以连接上socket服务了!
访问http://localhost:9527/chat.html
查看network,有ws标签,可以看到socket.io.js已经有了!
main.js继续写, 刷新chat.html页面,就可以看到数据了! - ‘欢迎加入聊天室’
1 2 3 4 5
| socket.on('sysMessage', (message) => { console.log(message) })
socket.emit('chatMessage', 'Hi'); // 发送给服务器!
|
server.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| // 监听,有没有连进来。 io.on('connection', (socket) => { socket.emit('sysMessage', '欢迎加入聊天室'); // 这里打印在ide里面,应该在浏览器里面 // 把接收到的信息,再发送出去! // socket.on('chatMessage', message => console.log(message)); // 接收 main.js发送的数据 socket.on('chatMessage', message => { //socket.emit('chatMessage', message) // 这里要改成 io来发送 - io全局对象!! // 开两个chat.html,一个刷新,另一个就能显示出来了!!! io.emit('chatMessage', message) // 这里要改成 io来发送 - io全局对象!! }); })
|
main.js里面监听 - 发送的信息..
1 2 3 4 5 6 7 8 9 10 11
| const socket = io();
socket.on('sysMessage', (message) => { console.log(message) })
socket.emit('chatMessage', 'Hi');
socket.on('chatMessage', message => { // 这里是由服务器返回的. console.log(message) });
|
这里添加html内容.
在chat.html里面,用form套住输入信息.
id=’chat-form’ // 找到位置 - 提交操作
id=’msg’ // 获取输入信息 - 发送出去.
1 2 3 4 5 6
| <form action="#" id='chat-form'> <div class="input-group mb-3"> <input id='msg' type="text" class="form-control" placeholder="请输入消息"> <button class="btn btn-success"><i class="bi bi-send"></i> 发送</button> </div> </form>
|
main.js - 发送消息,用的 chatMessage, 事件.
1 2 3 4 5
| document.getElementById('chat-form').addEventListener('submit', e => { e.preventDefault(); //组织默认提交事件. socket.emit('chatMessage', document.getElementById('msg').value); })
|
已经可以发送消息了.
现在,把信息显示在UI框架上面!
<div class="col-sm-12 col-md-8 message-container">
在容器上添加类,写入信息 - 做加法!
main.js - 已经可以发送到UI框架了!,,, 渐渐忘了 on监听的是什么了…..
1 2 3 4 5 6 7 8 9 10
| socket.on('chatMessage', message => { // console.log(message) document.querySelector('.message-container').innerHTML += `<div class="d-flex justify-content-end my-3"> <div class="card w-30 bg-success text-light"> <div class="card-body">${message}</div> </div> </div>` });
|
如何区分发送的是谁? - 已经可以发送人名了
用对象的形式发送,
{name: ‘’, content: ‘’};
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const name = 'John'
document.getElementById('chat-form').addEventListener('submit', e => { e.preventDefault(); //组织默认提交事件.
// socket.emit('chatMessage', document.getElementById('msg').value); socket.emit('chatMessage', { name, content: document.getElementById('msg').value }); })
document.querySelector('.message-container').innerHTML += `<div class="d-flex justify-content-end my-3"> <div class="card w-30 bg-success text-light"> <div class="card-body"> <div class='card-title'>${message.name}</div> <div class='card-text'>${message.content}</div> </div> </div> </div>`
|
如何动态获取姓名?
通过url
这里引入qs 插件 - 写入chat.html里面
<script src="https://cdnjs.cloudflare.com/ajax/libs/qs/6.10.3/qs.min.js"></script>
main.js里面 - 可以获取到url的信息了!
1 2 3 4 5
| // 如果没有{ignoreQueryPrefix: true},会多一个问号 // url - ?name=John const params = Qs.parse(location.search, {ignoreQueryPrefix: true})
console.log(params)//打印{name:'John'}
|
如何知道是不是自己发送的?
在发送的时候判断!
判断接收到的和url是不是一样 - 我发送出去的发送给自己 - 接收。
1 2 3 4 5 6 7 8 9 10 11
| const isMine = message.name === name; document.querySelector('.message-container').innerHTML += `<div class="d-flex ${isMine ? 'justify-content-end' : 'justify-content-start'} my-3"> <div class="card w-30 ${isMine ? 'bg-success text-light' : 'text-dark'} "> <div class="card-body"> <div class='card-title'>${isMine ? '我' : message.name}</div> <div class='card-text'>${message.content}</div> </div> </div> </div>`
|
如何处理用户列表?
定义一个user.js, 在server.js中导入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const users = [];
function userJoin(id, name, room) { // 加入用户列表 const user = {id, name, room}; users.push(user); return user; } function getRoomUsers(room) { // 返回在同一个房间的用户。 return users.filter(user => user.room === room); } module.exports = { userJoin, getRoomUsers }
|
main.js
1 2 3 4 5 6 7 8 9
| const name = params.name const room = params.room
socket.emit('joinRoom', {name, room}) // 发送
socket.on('roomUsers', userList => { // 接收当前房间的用户。 console.log(userList) })
|
server.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const {getRoomUsers, userJoin} = require('./user')
// 监听,有没有连进来。 io.on('connection', (socket) => { socket.on('joinRoom', ({name, room}) => { // 加入房间??? socket.join(room); userJoin(socket.id, name, room); // 把user加入users。。加入用户列表 // 全局变量,发送给当前room io.to(room).emit('roomUsers', getRoomUsers(room)) // 发送用户列表给客户端 socket.to(room).emit('sysMessage', '欢迎加入聊天室'); socket.on('chatMessage', message => { // 这个全局io 很重要... io.to(root).emit('chatMessage', message); } ); })
})
|
可以获取用户列表了,问题是刷新后,用户会再次加入 - 重复
因为刷新后,断开连接,又连接一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| user.js
//删除用户 function userLeave(id) { const index = users.findIndex(user => user.id === id); if(index !== -1) { return users.splice(index, 1)[0]; } } module.exports = { userJoin, getRoomUsers, userLeave }
-------------
server.js
// 监听系统事件。 socket.on('disconnect', () => { userLeave(socket.id); // 重新发送一份用户列表 io.to(room).emit('roomUsers', getRoomUsers(room)) // // 发送用户列表给客户端 })
|
获取了用户列表 - 写入页面!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| socket.on('roomUsers', userList => { // console.log(userList) const userListEl = document.querySelector('.user-list'); // ul的字段,写li进去 let listHtml = ''; userList.map(user => { listHtml += `<li class='list-group-item'>${user.name}</li>'` }) // 加入之前先清空 while(userListEl.childNodes.length > 3) { userListEl.removeChild(userListEl.lastChild); } userListEl.innerHTML += listHtml; })
|
填写房间名称 - 可以用列表
1 2 3 4 5 6
| const room = params.room // room名称 document.querySelector('.room-name').innerHTML = room;
如果换个room,bob也会走。
|
细节优化!
输入信息,不可输入空信息,发送后,信息框为空
1 2 3 4 5 6 7 8 9 10 11 12 13
| // socket.emit('chatMessage', document.getElementById('msg').value); const msgEl = document.getElementById('msg') if(msgEl.value === '') { alert('请输入信息'); return; } socket.emit('chatMessage', { name, content: msgEl.value }); msgEl.value = ''; // 发送完,消息设置为空
|
输入信息,滚动条应该在最下面!
1 2 3 4 5 6 7 8 9 10 11 12
| const scroll = function() { const rightPanel = document.querySelector('.right-panel'); // 最外层 const messageContainer = document.querySelector('.message-container'); // 信息区域 //scrollTop() 方法设置或返回被选元素的垂直滚动条位置。 // scrollHeight - 整体的高度! rightPanel.scrollTop = messageContainer.scrollHeight; // 到底了 }
在发送信息的时候,调用scroll
|
最后 - index.html
传递参数 - name / room
加个form, action - 指定跳转,method - 写入url!
<form action="chat.html" method="get">
给input指定name - name
给select指定name - room
完结!!!
还有任务:
进入页面 - 显示欢迎 **
可以在聊天室切换房间! ** 如何切换房间???
dom 元素什么时候获取??
server.js - 服务端 - 负责转发通信
main.js - 客户端 - 负责处理页面???
总结
Bootstrap构建页面,慢慢体会到好处了!
express - 开启服务,连接静态资源
http - 结合socket.io
socket.io - 转发通信
path - 处理本地资源
nodemon - 热加载 - 开发模式
socket.io,负责转发通信。有全局通信和局部通信之分。
主要通过on 和emit来触发,用回调函数传递信息。
步骤:
- 构建页面 - Bootstrap
- 建立连接 - express http + socket.io path
- 转发信息 - on emit
- 用js写入dom - innerHTML + 字符串拼接
- 创建user.js 来处理用户信息的资源获取与修改。
补充资料
构建即时聊天计数
- 长连接 - 占用资源,一条通信线路一直开着
- 轮询 - 请求量大,占用资源
- websocket - 客户端可向服务器发起请求,消耗资源小
network中查看socket 有id - 获取它 - 唯一性。