美文网首页
项目详细说明

项目详细说明

作者: 谷子多 | 来源:发表于2018-07-12 03:09 被阅读0次
      setTimeout(function(){
                window.dispatchEvent(new Event('resize'))
            },0)
    

    问:为什么这个项目需要redux:

    做项目的时候我通过不停总结,总算想通了:
    每次在页面发生跳转的时候,为了确保在下一次渲染正确的页面(每个发生跳转的页面内部都获取了redux的state,通过redirect字段来判断跳转),就必须更改redux的数据。
    还有其他的内容渲染也依赖最新redux数据!!所以你必须时时刻刻及时改变redux的数据!


    一、项目学习过程中的几点总结:

    1、登录注册的关键点 :
    redux里建立登录注册两个方法,通过该方法比较自身state和后台请求的数据是否相同(在组件内部调用该方法,参数是组件自己的sate),如验证通过,改变redux的state
    比如:genius/boss页面,底部icon是渲染boss还是genius,就是取决于state里的type字段。
    2、请求数据的组件:
    登录、注册(提交后台用户名、密码、身份)、完善信息(提交后台头像,描述等等)
    3、cookie :
    添加登录成功,后台设置cookie保存登录状态;2、写cookie是在登录后,读cookie在用户请求时,先读一下是否有cookie,
    所以刷新页面会停留在上次的页面,正是因为cookie设置的原因**。
    4、关于Ant Design
    该组件库没有实现和react-router-dom的切换路由的良好配合,所以平时我们是通过Link标签切换路由,而引用这个组件库的navbar时直接用this.props.history里push吧!


    二、页面逻辑讲解:

    页面跳转 :

    为什么每次跳转都正确的渲染了页面?
    redux的state设置了redirect,传入一个跳转方法。

    详解过程:在请求数据后,会在action里传入请求到的data,在redux的state里设置redirect(跳转字段),该字段接收一个页面跳转方法,参数为请求到的数据,该跳转方法的内部逻辑就是解耦请求到的data,获取type,通过该字段决定跳转到对应的页面(boss/genius,bossinfo/geniusinfo)。

    发生跳转的几个组件:

    1. 登录:完成提交了AUTH_SUCESS,改变了state的redirect,发生了跳转
    2. 注册:提交了AUTH_SUCESS,改变了state的redirect,发生了跳转
    3. 完善信息:提交了AUTH_SUCESS,改变了state的redirect,发生了跳转
      共同点:改变state,为redirectTo的绑定跳转函数,组件内部设置Redirect重定向组件,所以:正确的渲染了对应的页面。
     case AUTH_SUCESS:
                return {...state,msg:'',redirectTo:getRedirectPath(action.data),...action.data,pwd:''}
    
    export function getRedirectPath({type, avatar}){
        // 根据用户信息 返回跳转地址
        // user.type /boss /genius
        // user.avatar /bossinfo /geniusinfo 
        console.log(type)
        let url = (type==='boss')?'/boss': '/genius'
        if (!avatar) {
            url += 'info'
        }
        
        return url
    }
    
    

    要在各组件内放置:

    {this.props.redirectTo?<Redirect to="this.props.redirectTo"/>:null}
    

    登录:

    要做的事

    • 请求数据
    • 将请求到的数据合并到redux的state
    • 跳转页面。
      组件内代码讲解:设置自己的state(字段:user,pwd),在表单控件发生变化的同时setstate,点击登录按钮时传入redux的校验方法:login,用来判断用户密码是否正确。
      代码:
      1、redux内:
      actionType:AUTH_SUCESS
      actionCreator:
      authSucess(return {type:AUTH_SUCESS,data:data})
            export function login(data) {
                    return dispatch=>{
                        axios.post('./user/login',data).
                            then(res=>{
                        //请求成功,提交成功actionCreator:actionCreator
                        if(res.status === 200 && res.data.code===0){
                                dispatch(actionCreator(res.data.data))
                         }
                        // 请求失败,提交失败actionCreator:errorMsg
                        else{
                                dispatch(errorMsg(res.data,msg))
                        }
                    })
                 }
            }
            function reducer(state,action){
                    switch(action.type){
                            case AUTH_SUCESS:
                                return  {...state,msg:'',redirect:getRedirectPath(action.data),...action.data}
                        default:
                                return state
                     }
            }
    
    

    2、请求通过之后跳转页面。


    二、注册:

    1、逻辑和登录差不多,就不详细说了。(提交后台注册信息、跳转页面)


    三、完善信息页面

    两个组件 : geniusinfo/bossinfo
    流程
    用户添加头像和简介等相关信息(设置组件内部的state,设置对应字段) => 点击保存 => 请求接口、post传递数据(提交对应的action,传入请求到的值) => 更改redux(这一步是确保点击按钮后渲染到正确组件的关键!再次说明:跳转到哪是通过redirectTo决定的!!!)

    //组件内部
    this.state = {
                title : '',
                company : '',
                money : '',
                desc : ''
    }
    <Button  onClick={ ()=>{ this.props.update(this.state) }  }
      >保存</Button>
    // redux
    
    // 账号补全信息补全信息
    export function update(state) {
        return dispatch=>{
            axios.post('/user/update',state).
                then(res=>{
                    if(res.status === 200 && res.data.code===0){
                        // 后台返回的信息:user和用于跳转的type
                        dispatch(authSucess(res.data.data))
                        console.log(res.data.data)
                    }else{
                        dispatch(errorMsg(res.data.msg))
                    }
                })
        }
    }
    // reducer
      case AUTH_SUCESS:
        return {...state,msg:'',redirectTo:getRedirectPath(action.data),...action.data,pwd:''}
    

    四、头像选择组件:

    要给父组件bossinfo/geniusinfo传入选择的是哪一个图片,方便父组件记录到redux里面。

    其实是父组件想从子组件中拿到数据,那么也是一样,在父组件设置自定义属性,写一个方法,子组件通过props接收该方法并传入具体参数即可

    // 子组件:获取props
      <Grid 
       onClick={elm=>{
         this.props.selectAvator(elm.text)
       }}
      />  
    // 父组件传递
    <AvatarSelector
       selectAvator={(imgname)=>{
        this.setState({
        avatar : imgname
        })
      }}
    ></AvatarSelector>
    

    boss组件

    请求接口,获取genius数据后渲染到页面,请求到的数据都统一放到组件自身的state里,方便处理。然后将state的数据进行循环渲染即可!


    使用redux管理牛人列表

    因为BOSS和求职的卡片信息是一样的,所以我们可以将其封装成组件方便使用。

    将数据放到redux的步骤:

    // 1
    const USER_LIST = 'USER_LIST'
    
    const initState = {
        userList : []
    }
    // 3
    export function chatuser(state=initState,action){
        switch(action.type){
            case USER_LIST :
                // 这个状态随时可能加其他的数据,所以需要先展开
                return {...state,userList:action.payload}
            default:
                return state
        }
    }
    // 2
    export function userList(data){
        return {type:USER_LIST,payload:data}
    }
    // 4
    export function getUserList(type){
        return dispatch=>{
            axios.get('/user/list?type='+type).
            then(res=>{
                if(res.data.code==0){
                    dispatch(userList(res.data.data))
                }
            })
        }
    }
    

    boss列表和组件优化

    boss和gennius的用户展示是一样的,所以将用户展示这部分单独抽离成一个组件:usercard。


    个人中心信息展示

    我们之前已经把用户所有的数据,通过get请求'/user/info'接口,将获取的数据都保存在了redux的state中,在redux的调试工具中即可查看到。所以用户中心这个页面是非常好做的,直接从redux获取数据,展示出来,加上需要的按钮即可。


    在个人中心里退出登录

    关键是清除cookie。npm下载browserCookie,并在页面中引入。
    1、逻辑:点击退出登录按钮清除cookie

        logout(){
            console.log('logout')
            browserCookies.erase('userid')
        }
    

    2、具体实现
    使用Modal组件,点击时调用以下代码

    alert('注销', '确认退出登录吗', [
                { text: '取消', onPress: () => console.log('cancel') },
                { text: '确认', onPress: () => {
                    browserCookies.erase('userid')
                    window.location.href = window.location.href
                }},
            ])
    

    现在是清空cookie,刷新页面实现了跳转。
    这样的体验不好。
    所以我们在注销时清空redux数据这个方法较好。

      // 退出登录 :把初始信息放入
            case LOGOUT : 
                return {...initState,redirectTo:'/login'}
    

    Socket.io

    屏幕快照 2018-07-16 上午6.47.29.png
    屏幕快照 2018-07-16 上午6.49.33.png

    socket.io前后端联通,消息发送流程梳理

    注:socket是当前的请求,io是全局的

    流程
    1、后端io.on建立connection链接
    2、前端触发 socket.emit,方法:sendmsg,携带数据,发起成功之后,后端就可以收到消息了。
    3、后端socket.on监听前端发送的消息sendmsg,在方法体内发送到全局接收io.emit('recvmsg',data)
    4、前端在componentDidMount中socket.on监听recvmsg,获取data,将数据更新到组件中

    1、后端express : 引入socket.io,和express关联

    const app  = express()
    //work with express
    Const server = require('http').Server(app)
    const io = require('socket.io')(server)
    // 参数:socket,当前的链接;io,全局链接
    io.on('connection',function(socket){
        console.log('user login')
        // 监听前端发送的消息,方法就是前端的方法
        socket.on('sendmsg',function(data){
            const {form , to , msg} = data
            const chatid = [form,to].sort().join('_')
    
            // data是前端发送过来的消息
            console.log(data)
            // 发送到全局,每个人都是接受的状态
            //io.emit('recvmsg',data)
        })
    })
    server.listen(9093,function(){
        console.log('Node app start at port 9093')
    })
    

    2、前端
    发起链接,在conponentMount中,也可以放在外面直接引入;
    发起成功之后,后端就可以收到消息了。

    // socket客户端
    import io from 'socket.io-client'
    // 因为跨域了,所以需要写明链接的地址
    const socket = io('ws://localhost:9093')
    

    // 消息发送到后端
    handelSubmit(){
    //发送state到后端
    socket.emit('sendmsg',{text:this.state.text})
    }
    // 监听sendmsg事件,监控消息
    socket.on('recvmsg',function(data)){
    console.log(data)
    }
    3、聊天信息展示,在componentDidMount中监听

     componentDidMount(){
            //  socket.监听recvmsg,接收消息
            // socket.on('recvmsg',data=>{
               this.setState({
                    msg:[...this.state.msg,data.text]
                })
            })  
           // 加载消息列表   
            this.props.getMsgList()  
        }
    

    聊天页面redux链接 : chat.redux.js

    1、state设置chatmsg消息列表和unread未读信息字段;
    2、进入聊天页面,axios获取消息列表,并将信息并入state。

    !!!用户识别:使用用户的ID来识别用户
    1、发送方 : user中获取id
    2、接收方:在usercard组件中,在点击用户头像时,在地址栏中加入用户ID

    this.props.history.push(`/chat/${v._id}`)
    

    1.点击发送按钮,传给后台的字段 : from,to,msg
    2.组件即将挂载时recvMsg,渲染对话

    注意:后端返回的是全部消息,分组需要前后端协商将两个id拼接进行校验!
    点击提交,将from,to,msg传给后端,后端接收到之后将消息广播到全局。通过地址栏获取的id(to)和redux的to比较,如果相同,就说明是接收方,然后将这条消息显示在左边,否则就是自己的消息,显示在右边。通过对比chatId,就可以定位到这两个人的消息,避免消息错乱。

    消息列表:消息分组,将chatmsg(返回的是全部的消息)的数据根据chaid分组,这样就得到了对话分组。
    chatList=this.props.chat.chatmsg.filter( v=>v.chatid===chatid )

    未读消息数量:chatList(根据v.chatid分组)中 : to = 当前登录用户,说明消息是本人发送

    未读消息清零: 进入聊天页面,向后端发送请求,让后端标识一下,将read字段标识为true.
    后端返回from(对方),to(当前登录的userid)

    相关文章

      网友评论

          本文标题:项目详细说明

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