之前做了一个影集小应用,前端用的是react,后端用的是express+mongodb,最近把组件间的状态管理改成了redux,并添加了redux-saga管理异步操作,记录一些总结在线地址手机模式源码主要功能抓取豆瓣电影信息并进入MongoDB电影列表显示,分类,搜索电影详情显示及附件管理注册,登录权限控制,普通用户可以进入,收藏,管理员可以进入,修改,删除用户中心,我的收藏列表的一些总结前端使用react,redux加redux-saga,简单总结redux,记录一个前后端接口调用thathasdependencies问题redux一句话总结redux,我觉得是为了平衡组件间垂直的props传递和父子组件之间的状态爱恨纠葛,将一个垂直关系转化为多个组件和一个独立状态的对象交互直接,所以之后,代码结构确实看起来更清晰了。redux,action,reducer,storageaction的核心概念就是说明我要操作一个state,怎么操作是reducer的事,所有的state都存储在store里面,store发出action交给他们到指定的reducer去处理redux。强制规范我们对状态的操作只能在动作和缩减器中完成。这样一来,原来复杂的业务逻辑处理就变了,仅限于action和reducers,组件看起来非常干净。其实,复杂的东西放在哪里都复杂,现在却更清晰了。使用redux的缺点是定义各种action和连接各种组件过于繁琐。....现在又出现了一个Mobx,不知道严重到什么程度。反正大家都同意~redux-sagaredux-saga是用来处理异步调用的。借助生成器,异步代码看起来更加简洁。常用的有take,takeLatest,takeEvery,put,call,fork,select,在使用过程中,遇到接口调用有前后台依赖的问题,描述的比较有意思:有个接口/api/user/checkLogin,用于判断是否登录,在最外层的中触发组件componentDidMount中的action发起本次请求,如果接口返回login状态,还发送了一个获取用户信息的函数*checkLogin(){constres=yieldUtil.fetch('/api/user/checkLogin')yieldput(recieveCheckLogin(!res.code))if(!res.code){//登录yieldput(fetchUinfo())}}exportfunction*watchCheckLogin(){yieldtakeLatest(CHECK_LOAGIN,checkLogin)}然后我有一个电影详情页面组件。在该组件的componentDidMount中,会发起/api/movies/${id}接口获取电影信息。如果用户已登录,也会启动获取电影附件信息的方法。接口/api/movies/${id}/attach,整个步骤写在生成器中(id){returnyieldUtil.fetch(`/api/movies/${id}/attach`)}function*getMovieInfo(action){const{movieId}=action让{login}=yieldselect(state=>state.loginStatus)constres=yieldcall(getItemMovie,movieId)yieldput(recieveItemMovieInfo(res.data[0]))如果(res.data[0].attachId&&login){constattach=yieldcall(getMovieAttach,movieId)yieldput(recieveMovieAttach(attach.data[0]))}}exportfunction*watchLoadItemMovie(){yieldtakeLatest(LOAD_ITEM_MOVIE,getMovieInfo)}用户已登录并输入详情,流程正常,但是如果在详情页刷新了页面,并没有触发获取附件的接口。原因是checkLogin接口还没有返回结果,state.loginStatus状态还是false。上面没有去if。一开始,我想到了如何控制一些生成器中的yield顺序。sequence解决(如果用户没有登录,再发送一次CHECK_LOAGIN,结果返回给流程继续),但是有两次CHECK_LOAGIN调用,如果登录了,又会调用一次获取用户信息的接口,肯定notfunction*getMovieInfo(action){const{movieId}=action让{login}=yieldselect(state=>state.loginStatus)constres=yieldcall(getItemMovie,movieId)yieldput(recieveItemMovieInfo(res.data[0]))//if(!login){////刷新页面时,如果checklogin接口还没有返回数据或发送,应该触发一次checklogin////checklogin返回后才能获取登录状态//yieldput({//type:CHECK_LOAGIN//})//constret=yieldtake(RECIEVE_CHECK_LOAGIN)//login=ret.loginStatus//}if(res.data[0].attachId&&login){constattach=yieldcall(getMovieAttach,movieId)yieldput(recieveMovieAttach(attach.data[0]))}}最终的解决方案是分解生成器的职责,在componentWillUpdate//中适当触发获取附件的动作从getMovieInfo获取附件与生成器function*getMovieInfo(action){const{movieId}=actionconstres=yieldcall(getItemMovie,movieId)yieldput(recieveItemMovieInfo(res.data[0]))}function*watchLoadItemMovie(){yieldtakeLatest(LOAD_ITEM_MOVIE,getMovieInfo)}function*watchLoadAttach(){while(true){const{movieId}=yieldtake(LOAD_MOVIE_ATTACH)const{attachId}=yieldselect(state=>state.detail.movi??eInfo)常量附加=yieldcall(getMovieAttach,movieId)yieldput(recieveMovieAttach(attach.data[0]))}}//componentWillUpdate(nextProps){if(nextProps.loginStatus&&(nextProps.movi??eInfo!==this.props.movi??eInfo)){//是登录状态,当movieInfo返回时const{id}=this.props.match.paramsthis.props.loadMovieAttach(id)}}总结,合理使用组件钩子函数,不要在generator中处理太多操作,增加灵活性后端采用express和mongodb,也使用redis,主要是技术点是使用pm2管理node应用和部署代码,在mongodb中开启身份认证,使用token+redis进行身份认证,在node中编写单元测试。值得记录下使用jwt+redis做token-based用户身份认证是基于token的认证流程。客户端发起登录请求。服务器验证用户名和密码。验证成功。服务器生成令牌并响应客户端。令牌包含在客户端之后的每个请求的标头中。服务端需要认证接口需要验证token,如果验证成功,这里接收请求使用jsonwebtoken生成token,jwt.sign(payload,secretOrPrivateKey,[options,callback])使用express-jwt验证token(如果验证成功,token信息会放在request.user中)express_jwt({secret:SECRET,getToken:(req)=>{if(req.headers.authorization&&req.headers.authorization.split('')[0]==='Bearer'){returnreq.headers.authorization.split('')[1];}elseif(req.query&&req.query.token){返回req.query.token;}返回空值;为什么使用redis**可以指定token的有效期使用jsonwebtoken生成token,jsonwebtoken的verify方法也提供了更新token有效期的选项,但是这里使用了express_jwt中间件,express_jwt做的没有提供刷新token的方法**思路:客户端请求登录成功,生成token并将token保存在redis中,设置redis的有效期(比如1h),新的请求来了,首先,express_jwt验证token,验证成功,再验证redis中是否存在token。如果客户端在有效期内有新的请求,提取token,在redis中更新token的有效期,客户端退出请求并在redis中删除。这个token的具体代码使用了mocha+supertest+should来写单元测试。测试涵盖所有接口。开发过程中,因为没有进度要求,所以慢慢写。写完一个接口,再写一个测试。试卷写的还算详细整个过程挺有意思的。Mocha是一个node单元测试框架,类似于前端的jasmine,语法也类似于supertest。用来测试node接口的库应该是nodejs断言库,可读性很强的一个高阶测试的例子,篇幅太长,这里就不放了。最后,喜欢的话可以关注一下,万一有福利呢。...