内容简介:之前看一个写聊天器的教程,自己也跟着教程做了一遍,由于懒得去找图片和一些图标我就用教程中的素材来做,主要是用了1.首先在
一、项目预览
之前看一个写聊天器的教程,自己也跟着教程做了一遍,由于懒得去找图片和一些图标我就用教程中的素材来做,主要是用了 react+react-router+redux+Node.js+socket.io
的技术栈,接下来就是项目的预览
1.首先在 /login
下能看到有登录和注册按钮
2.点击注册按钮,路由跳到 /register
,注册一个账号,用户和密码都为LHH,选择“牛人”,点击注册,之后路由会跳到 /geniusinfo
,即牛人完善信息页,选择一个头像并完善信息后点击保存按钮
3.可以看到已经进入有三个tab选项的内容页面了,点击“我”,路由跳转到 /me
即可看到个人中心内容,但此时boss和消息的tab页仍没有内容,可以按照之前步骤注册一个Boss账号,只需在注册的时候选择Boss选项
4.现在在LHH和LCE账号分别能看到的列表
5.点击进入聊天室,输入内容
二、接下来对项目的主要内容进行解释
1.项目的除掉node_modules后的目录
├─build │ └─static │ ├─css │ └─js ├─config │ └─jest ├─public ├─scripts ├─server └─src ├─component │ ├─authroute │ ├─avatar-selector │ ├─boss │ ├─chat │ ├─dashboard │ ├─genius │ ├─img │ ├─logo │ ├─msg │ ├─navlink │ │ └─img │ ├─user │ └─usercard ├─container │ ├─bossinfo │ ├─geniusinfo │ ├─login │ └─register └─redux
其中 build
文件夹的内容为npm run build打包后的内容,在项目中如果启用后端接口也可访问
2.入口页面
import React from 'react'; import ReactDOM from 'react-dom'; import { createStore, applyMiddleware, compose } from 'redux'; import thunk from 'redux-thunk'; import { Provider } from 'react-redux'; // eslint-disable-next-line import { BrowserRouter } from 'react-router-dom'; import App from './app' import reducers from './reducer' import './config' import './index.css' const store = createStore(reducers, compose( applyMiddleware(thunk), window.devToolsExtension?window.devToolsExtension():f=>f )) // boss genius me msg 4个页面 ReactDOM.render( (<Provider store={store}> <BrowserRouter> <App></App> </BrowserRouter> </Provider> ), document.getElementById('root') )
使用react-redux的Provider,可实现全局的状态存储,子组件可通过props获得存储在全局的状态
const store = createStore(reducers, compose( applyMiddleware(thunk), window.devToolsExtension?window.devToolsExtension():f=>f ))
上面代码的主要作用是关于配置浏览器的redux插件的,可以通过这个插件在控制台中查看state中的数据。
来看下app.js中的代码
import React from 'react' import Login from './container/login/login.js'; import Register from './container/register/register.js'; import AuthRoute from './component/authroute/authroute.js'; import BossInfo from './container/bossinfo/bossinfo.js'; import Geniusinfo from './container/geniusinfo/geniusinfo'; import Dashboard from './component/dashboard/dashboard'; import Chat from './component/chat/chat' import { Route, Switch } from 'react-router-dom'; class App extends React.Component{ render() { return ( <div> <AuthRoute></AuthRoute> <Switch> <Route path='/bossinfo' component={BossInfo}></Route> <Route path='/geniusinfo' component={Geniusinfo}></Route> <Route path='/login' component={Login}></Route> <Route path='/register' component={Register}></Route> <Route path='/chat/:user' component={Chat}></Route> <Route component={Dashboard}></Route> </Switch> </div> ) } } export default App
这里主要是讲主页面中的代码分割出来。
在 authroute.js
中是路由跳转的逻辑判断
页面中的UI组件也用到了 antd-mobile
插件
客户端接收和传送数据得引入 socket.io-client
,代码在 chat.redux.js
中。
聊天器中需要存储在数据库的内容主要为 from
(发送端)、 to
(接收端)、 read
(是否已读)、 content
(聊天内容)、 create_time
(聊天时间)而且还需要一个唯一的 chatid
来代表这个聊天室的唯一性,可以用 from
和 to
拼接,拼接函数写在 util.js
中。
3.Server
后端接口用到了 node.js
的 express
框架,数据库用到了 mongodb
,在 server
文件夹中存放连接数据库的文件, model.js
在直接与 mongodb
数据库连接,
const mongoose = require('mongoose'); // 连接mongo,并且使用my_app这个集合 const DB_URL = "mongodb://localhost:27017/chat_app"; mongoose.connect(DB_URL); const models = { user: { 'user': { 'type': String, 'require': true }, 'pwd': { 'type': String, 'require': true }, 'type': { 'type': String, 'require': true }, // 头像 'avatar': { 'type': String }, // 个人简介或者职位简介 'desc': { 'type': String }, // 职位名 'title': { 'type': String }, // 如果是boss,还有两个字段 'company': { 'type': String }, 'money': { 'type': String } }, chat: { 'chatid': { 'type': String, 'require': true }, 'from': { 'type': String, 'rewuire': true }, 'to': { 'type': String, 'require': true }, 'read': { 'type': String, 'require': true }, 'content': { 'type': String, 'require': true, 'default': '' }, 'create_time': { 'type': Number, 'default': new Date().getTime() } } } for (let m in models) { mongoose.model(m, new mongoose.Schema(models[m])) } module.exports = { getModel: function(name) { return mongoose.model(name) } }
连接的数据库端口号为 27017
,这个视自己电脑的数据库端口号而定。
在 server.js
中引入了 http、express、socket.io
插件,服务端用的是 9093
端口,
const express = require('express'); const bodyParser = require('body-parser'); const cookieParser = require('cookie-parser'); const model = require('./model') // const User = model.getModel('user'); const Chat = model.getModel('chat'); const path = require('path') const app = express(); //work with express const server = require('http').Server(app); const io = require('socket.io')(server); io.on('connection', function(socket) { // console.log('user login') socket.on('sendmsg', function(data) { const { from, to, msg } = data; const chatid = [from, to].sort().join('_'); Chat.create({ chatid, from, to, content: msg }, function(err, doc) { // console.log(doc._doc) io.emit('recvmsg', Object.assign({}, doc._doc)) }) // console.log(data); // io.emit('recvmsg', data) }) }) const userRouter = require('./user'); app.use(cookieParser()); app.use(bodyParser.json()) app.use('/user', userRouter); app.use(function(req, res, next) { if (req.url.startsWith('/user/') || req.url.startsWith('/static/')) { return next() } return res.sendFile(path.resolve('build/index.html')) }) app.use('/', express.static(path.resolve('build'))) server.listen(9093, function() { console.log('Node app start at port 9093') });
客户端用到的接口写在 user.js
中
const express = require('express') const Router = express.Router(); const model = require('./model') const User = model.getModel('user'); const Chat = model.getModel('chat'); const _filter = { 'pwd': 0, '__v': 0 }; // 删除所有聊天记录 // Chat.remove({}, function(e, d) {}) // 加密 const utils = require('utility'); Router.get('/list', function(req, res) { const { type } = req.query // 删除所有用户 // User.remove({}, function(e, d) {}) User.find({ type }, _filter, function(err, doc) { return res.json({ code: 0, data: doc }) }) }); Router.get('/getmsglist', function(req, res) { const user = req.cookies.userid; User.find({}, function(err, userdoc) { let users = {}; userdoc.forEach(v => { users[v._id] = { name: v.user, avatar: v.avatar } }) Chat.find({ '$or': [{ from: user }, { to: user }] }, function(err, doc) { // console.log(doc) if (!err) { return res.json({ code: 0, msgs: doc, users: users }) } }) }) }) Router.post('/readmsg', function(req, res) { const userid = req.cookies.userid; const { from } = req.body; // console.log(userid, from) Chat.update({ from, to: userid }, { '$set': { read: true } }, { 'multi': true }, function(err, doc) { if (!err) { return res.json({ code: 0, num: doc.nModified }) } return res.json({ code: 1, msg: '修改失败' }) }) }) Router.post('/update', function(req, res) { const userid = req.cookies.userid; if (!userid) { return json.dumps({ code: 1 }); } const body = req.body; User.findByIdAndUpdate(userid, body, function(err, doc) { const data = Object.assign({}, { user: doc.user, type: doc.type }, body) return res.json({ code: 0, data }) }) }); Router.post('/login', function(req, res) { const { user, pwd } = req.body; User.findOne({ user, pwd: md5Pwd(pwd) }, _filter, function(err, doc) { if (!doc) { return res.json({ code: 1, msg: '用户名或者密码错误' }); } res.cookie('userid', doc._id) return res.json({ code: 0, data: doc }) }) }); Router.post('/register', function(req, res) { console.log(req.body); const { user, pwd, type } = req.body; User.findOne({ user }, function(err, doc) { if (doc) { return res.json({ code: 1, msg: '用户名重置' }) } const userModel = new User({ user, pwd: md5Pwd(pwd), type }); userModel.save(function(e, d) { if (e) { return res.json({ code: 1, msg: '后端出错了' }) } const { user, type, _id } = d; res.cookie('userid', _id) return res.json({ code: 0, data: { user, type, _id } }) }) }) }) Router.get('/info', function(req, res) { const { userid } = req.cookies; if (!userid) { return res.json({ code: 1 }) } User.findOne({ _id: userid }, _filter, function(err, doc) { if (err) { return res.json({ code: 1, msg: '后端出错了' }) } if (doc) { return res.json({ code: 0, data: doc }) } }) // 用户有没有cookie }); // 密码加盐 function md5Pwd(pwd) { const salt = 'lhh_is_good_1310486!@#5^%~*'; return utils.md5(utils.md5(pwd + salt)) } module.exports = Router
三、总结
本项目实现了获取数据和表现的代码分离,也是对于学习 React、Node和WebSocket
的一次更进一步提升,当然还有很多可以改进的地方,比如可以用 async
和 await
进行异步获取数据等等。
作为一名前端菜鸟,还是希望前辈能给一些学习的建议和指点迷津
最后附上本项目的代码链接
github链接以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- SWOOLE开发实时聊天系统(十)用户断开聊天
- 聊天私服 Akaxin 0.5.4 发布,优化聊天、注册逻辑
- Netty网络聊天(一) 聊天室的实战(最易懂)
- 基于Flutter+Dart聊天实例|flutter仿微信界面聊天室
- h5聊天室模板|仿微信聊天室html5
- 创建聊天机器人,第 3 部分: 使用认知(或人工智能)服务增强聊天机器人
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。