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服务器(9):实现非阻塞操作
Dec 18 NodeJs
浅析Nodejs npm常用命令
Jun 14 NodeJs
nodejs中模块定义实例详解
Mar 18 NodeJs
Nodejs+angularjs结合multiparty实现多图片上传的示例代码
Sep 29 NodeJs
nodejs基于mssql模块连接sqlserver数据库的简单封装操作示例
Jan 05 NodeJs
使用npm安装最新版本nodejs
Jan 18 NodeJs
对mac下nodejs 更新到最新版本的最新方法(推荐)
May 17 NodeJs
NodeJs项目中关闭ESLint的方法
Aug 09 NodeJs
Nodejs调用Dll模块的方法
Sep 17 NodeJs
nodejs读取本地中文json文件出现乱码解决方法
Oct 10 NodeJs
NodeJS搭建HTTP服务器的实现步骤
Oct 12 NodeJs
使用nodeJS中的fs模块对文件及目录进行读写,删除,追加,等操作详解
Feb 06 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
php生成EXCEL的东东
2006/10/09 PHP
分享一下贝贝成长进度的php代码
2012/09/14 PHP
php检测文件编码的方法示例
2014/04/25 PHP
php socket实现的聊天室代码分享
2014/08/16 PHP
PHP-Java-Bridge使用笔记
2014/09/22 PHP
PHP 数组基本操作方法详解
2016/06/17 PHP
jQuery 入门级学习笔记及源码
2010/01/22 Javascript
JavaScript.Encode手动解码技巧
2010/07/14 Javascript
THREE.JS入门教程(4)创建粒子系统
2013/01/24 Javascript
DOM基础教程之使用DOM设置文本框
2015/01/20 Javascript
深入理解JavaScript编程中的原型概念
2015/06/25 Javascript
jQuery 选择符详细介绍及整理
2016/12/02 Javascript
脚本div实现拖放功能(两种)
2017/02/13 Javascript
Angular4的输入属性与输出属性实例详解
2017/11/29 Javascript
javascript实现遮罩层动态效果实例
2019/05/14 Javascript
ES6 Generator基本使用方法示例
2020/06/06 Javascript
Vue实现todo应用的示例
2021/02/20 Vue.js
Django集成百度富文本编辑器uEditor攻略
2014/07/04 Python
Python中DJANGO简单测试实例
2015/05/11 Python
在pyqt5中QLineEdit里面的内容回车发送的实例
2019/06/21 Python
用Python爬取QQ音乐评论并制成词云图的实例
2019/08/24 Python
python隐藏类中属性的3种实现方法
2019/12/19 Python
PyCharm2019 安装和配置教程详解附激活码
2020/07/31 Python
css3 实现圆形旋转倒计时
2018/02/24 HTML / CSS
HTML5触摸事件演化tap事件介绍
2016/03/25 HTML / CSS
SHEIN香港:价格实惠的女性时尚服装
2018/08/14 全球购物
如何整合JQuery和Prototype
2014/01/31 面试题
信息与计算机科学职业规划范文:成为一艘有方向的船
2014/09/11 职场文书
群众路线教育实践活动剖析材料
2014/09/30 职场文书
人事局接收函
2015/01/31 职场文书
办公室个人总结
2015/02/28 职场文书
2015年电教工作总结
2015/05/26 职场文书
2015中学学校工作总结
2015/07/20 职场文书
Mysql关于数据库是否应该使用外键约束详解说明
2021/10/24 MySQL
Python 数据可视化之Bokeh详解
2021/11/02 Python
《LOL》“克隆大作战”久违归来 幻灵战队皮肤上线
2022/04/03 其他游戏