最近和后端同学I’M渣渣一起完成了一个Demo级的多人聊天室应用,实现了聊天室的基本功能。
采用前后端分离方案,我负责前端代码的编写,I’M渣渣实现了后端的接口。
预览地址:多人聊天室
源码仓库:[GitHub]
前端:
Create-React-App
redux
+ react-redux
等React-Router
、路由鉴权AntD
组件库 (Icon
图标、Notification
通知提醒框、Message
全局提示)这里仅记录了前端的相关技术,后端基于TCP
/HTTP
协议,采用C/C++实现,详细请查看I’M渣渣。
将用户的登录状态放入redux
,默认为false
未登录。
当App
组件挂载时,判断用户登录状态,若已经登录,则直接跳转到聊天室页面
;若未登录,则跳转到注册/登录页面
,让用户登录。
<Switch> {this.props.loginState ? ( <Fragment> <Route path="/room" component={Room} /> <Redirect to="/room" /> </Fragment> ) : ( <Fragment> <Route path="/welcome" component={Welcome} /> <Redirect to="/welcome" /> </Fragment> )} </Switch>
需要验证用户填写的用户名、密码是否符合规范,若不符合规范给出提示并直接return
,若符合规范才发送注册请求给服务器。
// 点击按钮,登录 register = () => { // 数字字母组合,字母开头 const unameReg = /^[a-zA-Z][a-zA-Z0-9]{2,9}$/; const uname = this.uname.value; const pwd = this.pwd.value; const pwdAgian = this.pwdAgian.value; // 判断用户名是否符合规则 if (!unameReg.test(uname)) { this.openUnameError(); return; } // 判断密码长度 if (!(pwd.length >= 6 && pwd.length <= 16)) { this.openPwdError(); return; } // 判断两次输入的密码是否一致 if (pwd !== pwdAgian) { this.openPwdUnEqual(); return; } // 调用接口,发送注册请求 const url = `${constUrl}/register`; axios({ method: 'get', url, params: { name: uname, pwd, }, }) .then(res => { // 注册成功 if (res.data.register === 0) { this.openRegisterSuccess(); this.uname.value = ''; this.pwd.value = ''; this.pwdAgian.value = ''; // 跳转到登录页 this.props.history.replace(`/welcome/login`); } else { // 注册失败,打开相应的提示框 switch (res.data.error) { case 0: { this.openUnameReuse(); return; } default: { this.openOtherError(); return; } } } }) .catch(err => console.error(err)); };
登录功能没什么好说的,收集参数并发送请求就可以了,若登录成功,则给出提示,并更改redux
中的登录状态为true
,页面自动会跳转到聊天室页面。若登陆失败,则根据服务器返回的数据,给出相应提示。
login = async () => { const url = `${constUrl}/login`; const name = this.inputUname.value; const pwd = this.inputPwd.value; axios({ method: 'get', url, params: { name, pwd, }, }) .then(res => { if (res.data.login === 0) { this.props.login(name); this.openLoginSuccess(); return; } switch (res.data.error) { case 0: { this.openUnameNotFound(); this.props.logout(); return; } case 1: { this.openPwdError(); this.props.logout(); return; } case 2: { this.openOtherError(); this.props.logout(); return; } default: return; } }) .catch(err => console.error(err));
接受消息功能,同样也是向服务器发送请求,获得所需消息,并展示在页面上。
这里需要将展示聊天记录容器的滚动条自动调整到底部,详情见JavaScript 实现容器滚动条默认出现在底部位置。
由于发送一次接收消息的请求,只能得到一次消息,所以我在componentDidMount()
生命周期函数中,开启了一个定时器,每隔1秒就发送一次请求,获得所有消息,展示到页面上。由于React的diff
算法,即使没有新的消息,频繁地发送请求,也不会有浪费太多性能。记得在componentWillUnmount()
中,清除定时器。
componentDidMount() { this.getMsg(); this.msgUpdate = setInterval(() => { this.getMsg(); }, 1000); } componentWillUnmount() { // 清除定时器 clearInterval(this.msgUpdate); }
// 获取所需消息 getMsg = () => { const url = `${constUrl}/information`; const name = this.props.name; axios({ method: 'get', url, params: { name }, // responseType: 'blob', }) .then(res => { console.log(res); // 获取人数 const userCount = res.data.userCount; this.setState({ userCount }); // 获取新数据 const newMsg = res.data.msg; // 获取原数据 const oldMsg = this.state.msg; if (res.data.information === 0) { this.setState({ msg: [...oldMsg, ...newMsg] }, () => { // 滚动条自动到底部 this.messageBox.scrollTop = this.messageBox.scrollHeight; }); } }) .catch(err => console.error(err)); };
发送请求之前,先判断输入框是否为空。不为空,再进行下一步,发送请求。
成功发送消息后,调用一次接收消息
函数,获得所有消息,即可将刚刚发送的消息立即展示到页面。
// 发送消息 sendMsg = () => { const msg = this.editMsg.value; const name = this.props.name; if (msg === '') { message.warning('请输入消息!'); return; } const url = `${constUrl}/information`; axios({ method: 'post', url, params: { msg, name, time: new Date().getTime(), id: nanoid(), }, }) .then(res => { // console.log(res); if (res.data.msg === 0) { this.editMsg.value = ''; message.success('发送成功!'); // 调用获取所有消息函数 this.getMsg(); } }) .catch(err => console.error(err));
退出时,发送请求给服务器,告诉服务器有用户退出了、哪个用户退出了,主要用于展示当前在线人数。
将redux
中的登录状态改为false
,页面自动跳转到注册/登录
页面。
logout = () => { const url = `${constUrl}/logout`; const name = this.props.name; axios({ method: 'get', url, params: { name, }, }); this.openLogout(); this.props.logout(); };
由于本人能力有限,本Demo还有一些BUG
没有解决:
退出
按钮,直接关闭浏览器页面,没有监听到用户的退出动作。空格
、?
、=
、#
等字符会显示其编码,无法正常显示。将来可能添加的功能:
采用token
验证用户身份
自定义背景图片切换
支持黑暗模式
适配移动端
支持上传头像
支持发送图片
…