美文网首页
react+react-router+redux+Node.js

react+react-router+redux+Node.js

作者: LHH大翰仔仔 | 来源:发表于2019-05-02 16:48 被阅读0次

    一、项目预览

    之前看一个写聊天器的教程,自己也跟着教程做了一遍,由于懒得去找图片和一些图标我就用教程中的素材来做,主要是用了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来代表这个聊天室的唯一性,可以用fromto拼接,拼接函数写在util.js中。

    3.Server

    后端接口用到了node.jsexpress框架,数据库用到了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的一次更进一步提升,当然还有很多可以改进的地方,比如可以用asyncawait进行异步获取数据等等。
    作为一名前端菜鸟,还是希望前辈能给一些学习的建议和指点迷津
    最后附上本项目的代码链接
    github链接

    相关文章

      网友评论

          本文标题:react+react-router+redux+Node.js

          本文链接:https://www.haomeiwen.com/subject/bvwnnqtx.html