nodejs和react实现即时通讯简易聊天室功能


Posted in NodeJs onAugust 21, 2019

npx create-react-app socketio-demo

进入socketio-demo目录 运行eject进行拆包,本项目也可以不拆,这是个人习惯。 注意如果运行eject命令最好在项目初始阶段执行,已经开始编写后不要再使用容易出现bug,新人谨慎使用eject命令

yarn eject

项目拆包后创建服务器文件夹和文件

mkdir server
type null>index.js

创建完成后目录如下

nodejs和react实现即时通讯简易聊天室功能 

编写即时通讯(聊天室)后台

安装nodejs插件

npm i express http socket.io nodemon

进入server文件夹下的index.js页面开始编写后台程序

const app = require('express')(); 
const server = require('http').Server(app); 
const io = require('socket.io')(server); 
//设置端口9093 
server.listen(9093); 
//创建socket.io连接 
io.on('connection', function (socket) { 
 //获取messages事件 
 socket.on('messages', function (data) { 
  //向所有连接进行广播 
 socket.broadcast.emit('messages', data) 
  //对发出者进行广播,用户名加上我 
 data.user=data.user+'[我]' 
 socket.emit('messages', data) 
 }); 
});

编写即时通讯(聊天室)前台

后台编写完毕,可以在src目录中编写前台内容 安装需要用到的react-router和redux依赖

npm i redux react-redux react-router react-router-dom

在src中创建io文件夹 在io文件夹中创建所需要的文件

cd src
mkdir io
cd io
type null>login.js
type null>socket-demo.js
type null>socket-demo.css
mkdir auth
cd auth
type null>auth.js

创建完成后目录如下

nodejs和react实现即时通讯简易聊天室功能 

这里auth.js文件是用来判断用户是否输入昵称,如已输入昵称可以进入聊天室,如没有输入昵称则跳回登录界面要求输入昵称

本项目当中我们把昵称存在redux里实现登录界面和聊天室界面的共用,当然现这个项目比较小,如果想用localStorage存在本地也可以,不过考虑到后期的扩展性以及加深对redux的理解我还是选择存在redux当中

src文件夹下创建redux.js文件

src文件夹下创建redux文件夹,在redux文件夹下创建user.redux.js文件

cd src
type null>redux.js
mkdir redux
cd redux
type null>user.redux.js

新建目录如下

nodejs和react实现即时通讯简易聊天室功能 

在redux文件夹下的user.redux.js中创建存储用户昵称的reducer

const SET_USERNAME='SET_USERNAME' 
//初始化仓库 
const initState={user:''} 
//根据动作改变仓库  
export function User(state = initState, action) { 
 switch (action.type) { 
  case SET_USERNAME: 
   return {...state,user:action.payload} 
  default: 
   return state 
 } 
} 
//写入昵称动作 
export function setUserName(user) { 
 return { 
  type:SET_USERNAME, 
 payload:user 
 } 
}

在src/redux.js文件中创建仓库 combineReducers用于多个reducer的合并,这个项目中也可以不加,单为了后期扩展加入使用

import { combineReducers, createStore } from 'redux' 
import {User} from './redux/user.redux' 
//window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 用于chrome redux的扩展项
let reducer = combineReducers({ User }) 
let store = createStore( 
 reducer,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()) 
 
export default store

这样就可以在页面当中使用redux了

下一步在app.js中引入redux,并把路由搭建起来 在src/app.js中写入

import React from 'react';
import {HashRouter as Router,Route,Switch} from 'react-router-dom'
import Login from "./io/login";
import SocketDemo from "./io/socket-demo";
import {Provider} from 'react-redux'
import store from './redux'
import Auth from "./io/auth/auth";
function App() {
 return (
  <Provider store={store}>
   <Router>
    <Auth></Auth>
    <Switch>
     <Route exact path='/' component={Login}/>
     <Route exact path='/talk' component={SocketDemo}/>
    </Switch>
   </Router>
  </Provider>
 );
}

export default App;

在写页面之前我们先安装修饰符插件

npm i babel-plugin-transform-decorators-legacy

Babel >= 7.x 时安装 @babel/plugin-proposal-decorators

npm i @babel/plugin-proposal-decorators

在package.json中babel项中配置,注意plugins放在presets前否则容易报错

"babel": { 
 "plugins": [ 
  ["@babel/plugin-proposal-decorators", { "legacy": true }] 
 ], 
 "presets": [ 
  "react-app" 
 ] 
}

好了这样就可以使用装饰付了

下面我们来编写判断是否设置用户名的程序 打开src/io/auth下的auth.js文件

import React from 'react'; 
import {connect} from 'react-redux' 
import {withRouter} from 'react-router-dom' 
//获取reducer 
@connect( 
 state=>state, 
 {} 
) 
//获取router 
@withRouter 
class Auth extends React.Component{ 
 componentDidMount() { 
  //如果有用户名就跳到聊天页,如没有则跳到登陆页。 
 if(this.props.User.user){ 
   this.props.history.push('/talk') 
  }else { 
   this.props.history.push('/') 
  } 
 } 
 render() { 
  return null 
 } 
} 
 
export default Auth

编写输入昵称并跳转步骤 打开src/io/login.js文件

import React from 'react';
import './socket-demo.css';
import {connect} from 'react-redux'
import {setUserName} from '../redux/user.redux'
@connect(
 null,
 {setUserName}
)
class Login extends React.Component{
 constructor(props) {
  super(props);
  this.state={
   user:''
  }
  this.login=this.login.bind(this)
  this.onKeyDown=this.onKeyDown.bind(this)
 }
 //键盘点击跳转 
 onKeyDown(e){
  switch (e.keyCode) {
   case 13:
    this.login();
    return;
   default:
    return;
  }
 }
 //添加键盘事件 
 componentDidMount() {
  document.addEventListener("keydown", this.onKeyDown)
 }
 //赋值state 
 handleChange(title,target){
  this.setState({
   [title]:target.target.value
  })
 }
 //赋值并跳转到聊天室页面 
 login(){
  let {user}=this.state;
  if(user!==null && user.trim()!==''){
   this.props.setUserName(user);
   this.props.history.push('/talk')
  }
 }
 render() {
  return (
   <div className='loginDiv'>
    <input type='text' placeholder='输入昵称' onChange={v=>this.handleChange('user',v)} />
    <button onClick={this.login}>进入聊天室</button>
   </div> );
 }
}

export default Login

下面是重头戏,聊天室的前端展示的核心代码 打开src/iosocket-demo.js文件

import React from 'react'
import io from 'socket.io-client'
import {connect} from 'react-redux'
import './socket-demo.css'
const url='ws://localhost:9093'
const socket = io(url);
@connect(
 state=>state,
 {}
)
class SocketDemo extends React.Component{
 constructor(props) {
  super(props);
  this.state={
   message:'',
   user:this.props.User.user,
   messages:[]
  }
  this.send=this.send.bind(this)
  this.login=this.login.bind(this)
  this.onKeyDown=this.onKeyDown.bind(this)
 }
 componentDidMount() {
  //输入欢迎信息 
  this.login()
  //增加回车事件 
  document.addEventListener("keydown", this.onKeyDown)
  //socket.io连接后台 
  io(url).on('connect', ()=>{
   console.log('connect');
   socket.on('messages', data => {
    //返回用户列表 
    this.setState({
     messages:[...this.state.messages,data]
    })
    if(this.refs.showDiv){
     this.refs.showDiv.scrollTop=2000
    }
   });
  });
 }
 componentWillUnmount() {
  //断开socket io连接 
  io('ws://localhost:9093').on('disconnect', function(){
   console.log('disconntect');
  });
  document.removeEventListener("keydown", this.onKeyDown)
 }
 //鼠标回车事件 
 onKeyDown(e){
  switch (e.keyCode) {
   case 13:
    this.send();
    return; default:
    return;
  }
 }
 //向后台发送信息 
 send(){
  let {user,message}=this.state;
  console.log(this.refs.showDiv);
  socket.emit('messages', {user,message});
  this.setState({
   message:''
  })
 }
 login(){
  let user=this.props.User.user;
  const obj={user:'作者',message:`欢迎${user}来到聊天室`}
  if(user.trim()!==''){
   this.setState({
    user:user,
    messages:[obj]
   })
  }
 }
 //赋值state 
 handleChange(title,target){
  this.setState({
   [title]:target.target.value
  })
 }
 render() {
  let cn='showInfo'
  return (
   <div>
    <div className='talkDiv'>
     <div className='operatingDiv'>
      <input type='text'
          placeholder='请在此输入聊天信息'
          onChange={v=>this.handleChange('message',v)}
          value={this.state.message}
      />
      <button onClick={this.send}>发送链接</button>
     </div> <div ref='showDiv' className='showDiv'>
     {
      this.state.messages.map((v,index)=>{
       if(index===0){
        cn='titleInfo'
       }else{
        cn='showInfo'
       }
       return (
        <div className={cn} key={index}>
         <span>{v.user}:</span>
         <span>{v.message}</span>
        </div> )
      })
     }
    </div>
    </div>
   </div> );
 }
}
export default SocketDemo;

最后加上src/iosocket-demo.css

body{ 
  background: #008DB7; 
 font-family: 'Microsoft YaHei UI'; 
 
} 
.loginDiv{ 
  text-align: center; 
 margin: 150px auto 0; 
 width: 250px; 
} 
.loginDiv input[type='text']{ 
  display: inline-block; 
 box-sizing: border-box; 
 border-radius: 5px; 
 padding-left: 5px; 
 border: none; 
 width: 250px; 
 height: 35px; 
 line-height: 35px; 
} 
.loginDiv button{ 
  display: inline-block; 
 box-sizing: border-box; 
 border-radius: 5px; 
 padding-left: 5px; 
 border: none; 
 width: 250px; 
 height: 35px; 
 line-height: 35px; 
 margin-top: 10px; 
 background: #0067A2; 
 color: #ffffff; 
} 
 
.talkDiv{ 
  position: fixed; 
 top: 0; 
 left: 0; 
 right: 0; 
 bottom: 0; 
} 
 
.talkDiv .operatingDiv{ 
  position: fixed; 
 bottom: 0; 
 left: 0; 
 right: 0; 
 height: 40px; 
 display: flex; 
} 
 
.talkDiv .operatingDiv input[type='text']{ 
  flex: 1; 
 height: 40px; 
 line-height: 40px; 
 box-sizing: border-box; 
 padding-left: 10px; 
} 
.talkDiv .operatingDiv button{ 
  display: inline-block; 
 box-sizing: border-box; 
 border-radius: 5px; 
 border: none; 
 width: 250px; 
 height: 40px; 
 line-height: 40px; 
 background: #0067A2; 
 color: #ffffff; 
} 
 
.talkDiv .showDiv{ 
  position: fixed; 
 bottom: 40px; 
 left: 0; 
 right: 0; 
 top: 0; 
 font-size: 16px; 
 color: #ffffff; 
 overflow: auto; 
} 
.talkDiv .showDiv .titleInfo{ 
  padding: 10px; 
 color: yellow; 
 font-size: 20px; 
} 
.talkDiv .showDiv .showInfo{ 
  padding: 10px; 
}

在package.json中加入命令行

"scripts": { 
 "start": "node scripts/start.js", 
 "build": "node scripts/build.js", 
 "server": "nodemon server/index.js"
},
  • 运行后台 yarn server
  • 运行前台 yarn start

启动程序

总结

以上所述是小编给大家介绍的nodejs和react实现即时通讯简易聊天室功能,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

NodeJs 相关文章推荐
Nodejs极简入门教程(三):进程
Oct 27 NodeJs
Nodejs学习笔记之NET模块
Jan 13 NodeJs
nodeJS代码实现计算交社保是否合适
Mar 09 NodeJs
解决nodejs中使用http请求返回值为html时乱码的问题
Feb 18 NodeJs
nodejs操作mysql实现增删改查的实例
May 28 NodeJs
详解nodeJs文件系统(fs)与流(stream)
Jan 24 NodeJs
nodejs实现范围请求的实现代码
Oct 12 NodeJs
Nodejs模块的调用操作实例分析
Dec 25 NodeJs
详解NodeJS Https HSM双向认证实现
Mar 12 NodeJs
独立部署小程序基于nodejs的服务器过程详解
Jun 24 NodeJs
nodejs使用node-xlsx生成excel的方法示例
Aug 22 NodeJs
nodejs开发一个最简单的web服务器实例讲解
Jan 02 NodeJs
Nodejs 识别图片类型的方法
Aug 15 #NodeJs
NodeJs实现简易WEB上传下载服务器
Aug 10 #NodeJs
NodeJs 实现简单WebSocket即时通讯的示例代码
Aug 05 #NodeJs
Nodejs监听日志文件的变化的过程解析
Aug 04 #NodeJs
nodejs对项目下所有空文件夹创建gitkeep的方法
Aug 02 #NodeJs
nodejs读取图片返回给浏览器显示
Jul 25 #NodeJs
关于NodeJS中的循环引用详解
Jul 23 #NodeJs
You might like
NO3第三帝国留言簿制作过程
2006/10/09 PHP
PHPMailer邮件类利用smtp.163.com发送邮件方法
2008/09/11 PHP
PHP fopen 读取带中文URL地址的一点见解
2012/09/25 PHP
php使用str_replace实现输入框回车替换br的方法
2014/11/24 PHP
php命名空间设计思想、用法与缺点分析
2019/07/17 PHP
动手学习无线电
2021/03/10 无线电
jquery实现表格奇数偶数行不同样式(有图为证及实现代码)
2013/01/23 Javascript
jQuery动态设置form表单的enctype值(实现代码)
2013/07/04 Javascript
js截取小数点后几位的写法
2013/11/14 Javascript
解决js下referer兼容各大浏览器的方法
2014/11/03 Javascript
jQuery中detach()方法用法实例
2014/12/25 Javascript
js判断鼠标左、中、右键哪个被点击的方法
2015/01/27 Javascript
js实现单击图片放大图片的方法
2015/02/17 Javascript
在AngularJS中使用AJAX的方法
2015/06/17 Javascript
JS实现的tab切换选项卡效果示例
2017/02/28 Javascript
详解angular笔记路由之angular-router
2017/09/12 Javascript
微信小程序如何使用canvas二维码保存至手机相册
2019/07/15 Javascript
解决echarts数据二次渲染不成功的问题
2020/07/20 Javascript
Vuex实现简单购物车
2021/01/10 Vue.js
[03:48]显微镜下的DOTA2第四期——TP动作
2014/06/20 DOTA
由Python运算π的值深入Python中科学计算的实现
2015/04/17 Python
python实现查找两个字符串中相同字符并输出的方法
2015/07/11 Python
python访问抓取网页常用命令总结
2017/04/11 Python
详解用pyecharts Geo实现动态数据热力图城市找不到问题解决
2019/06/26 Python
Python实现Selenium自动化Page模式
2019/07/14 Python
python3 enum模块的应用实例详解
2019/08/12 Python
Python生成器实现简单&quot;生产者消费者&quot;模型代码实例
2020/03/27 Python
域名注册、建站工具、网页主机、SSL证书:Dynadot
2017/01/06 全球购物
微软瑞士官方网站:Microsoft瑞士
2018/04/20 全球购物
Foot Locker德国官方网站:美国运动服和鞋类零售商
2018/11/01 全球购物
澳大利亚体育和露营装备在线/实体零售商:Find Sports
2020/06/03 全球购物
高级电工工作职责
2013/11/21 职场文书
料理师求职信
2014/01/30 职场文书
征婚广告词
2014/03/17 职场文书
奥巴马上海演讲稿
2014/09/10 职场文书
实体类或对象序列化时,忽略为空属性的操作
2021/06/30 Java/Android