摘要:其实,该复杂的东西在哪放都复杂,只不过现在更清晰一点使用不好的地方就是太繁琐了,定义各种各种组件。。。。。
之前做了个好电影搜集的小应用,前端采用react,后端采用express+mongodb,最近又将组件间的状态管理改成了redux,并加入了redux-saga来管理异步操作,记录一些总结在线地址 手机模式
源码
主要功能爬取豆瓣电影信息并录入MongoDB
电影列表展示,分类、搜索
电影详情展示及附件管理
注册、登录
权限控制,普通用户可以录入、收藏,administrator录入、修改、删除
用户中心,我的收藏列表
一些总结 前端前端使用了react,redux加redux-saga,对redux简单总结一下,同时记录一个前后接口调用有依赖关系的问题
redux
一句话总结redux,我觉的就是将组件之间的纵向的props传递和父子组件间的state爱恨纠缠给打平了,将一种纵向关系转变成多个组件和一个独立出来的状态对象直接交互,这样之后,代码结构确实看上去更加清晰了。
redux的核心概念,action,reducer,和store
action就是说明我要操作一个状态了,怎么操作是reducer的事,而所有状态存储在store中,store发出动作并交由指定的reducer来处理
redux强制规范了我们对状态的操作,只能在action和reducer这些东西中,这样,原本错综复杂的业务逻辑处理就换了个地,限制在了action和reducer中,组件看上去就很干净了。其实,该复杂的东西在哪放都复杂,只不过现在更清晰一点
使用redux不好的地方就是太繁琐了,定义各种action,connect各种组件。。。。。现在又出来一个Mobx,不明觉厉,反正大家都说好~
redux-saga
redux-saga用来处理异步调用啥的,借助于generator,让异步代码看起来更简洁,常用的有take,takeLatest,takeEvery,put,call,fork,select,使用过程中遇到一个接口调用有前后依赖关系的问题,比较有意思
描述一下:
有一个接口/api/user/checkLogin,用来判断是否登录,在最外层的
function* checkLogin() { const res = yield Util.fetch("/api/user/checkLogin") yield put(recieveCheckLogin(!res.code)) if (!res.code) { //已登录 yield put(fetchUinfo()) } } export function* watchCheckLogin() { yield takeLatest(CHECK_LOAGIN, checkLogin) }
然后我有一个电影详情页组件,在这个组件的componentDidMount中会发起/api/movies/${id}接口获取电影信息,如果用户是登录状态的话,还会发起一个获取电影附件信息的接口/api/movies/${id}/attach,整个步骤写在一个generator中
function* getItemMovie(id) { return yield Util.fetch(`/api/movies/${id}`) } function* getMovieAttach(id) { return yield Util.fetch(`/api/movies/${id}/attach`) } function* getMovieInfo(action) { const { movieId } = action let { login } = yield select(state => state.loginStatus) const res = yield call(getItemMovie, movieId) yield put(recieveItemMovieInfo(res.data[0])) if (res.data[0].attachId && login) { const attach = yield call(getMovieAttach, movieId) yield put(recieveMovieAttach(attach.data[0])) } } export function* watchLoadItemMovie() { yield takeLatest(LOAD_ITEM_MOVIE, getMovieInfo) }
用户登录了,进到详情,流程正常,但如果在详情页刷新了页面,获取附件的接口没触发,原因是此时checkLogin接口还没返回结果,state.loginStatus状态还是false,上面就没走到if中
一开始想着怎么控制一些generator中yield的先后顺序来解决(如果用户没有登录的话,再发一个CHECK_LOAGIN,结果返回了流程再继续),但存在CHECK_LOAGIN调用两次,如果登录了,还会再多一次获取用户信息的接口调用的情况,肯定不行
function* getMovieInfo(action) { const { movieId } = action let { login } = yield select(state => state.loginStatus) const res = yield call(getItemMovie, movieId) yield put(recieveItemMovieInfo(res.data[0])) // if (!login) { // //刷新页面的时候,如果此时checklogin接口还没返回数据或还没发出,应触发一个checklogin // //checklogin返回后才能得到login状态 // yield put({ // type: CHECK_LOAGIN // }) // const ret = yield take(RECIEVE_CHECK_LOAGIN) // login = ret.loginStatus // } if (res.data[0].attachId && login) { const attach = yield call(getMovieAttach, movieId) yield put(recieveMovieAttach(attach.data[0])) } }
最终的办法,分解generator的职责,componentWillUpdate中合适的触发获取附件的动作
//将获取附件的动作从 getMovieInfo这个generator中分离出来 function* getMovieInfo(action) { const { movieId } = action const res = yield call(getItemMovie, movieId) yield put(recieveItemMovieInfo(res.data[0])) } function* watchLoadItemMovie() { yield takeLatest(LOAD_ITEM_MOVIE, getMovieInfo) } function* watchLoadAttach() { while (true) { const { movieId } = yield take(LOAD_MOVIE_ATTACH) const { attachId } = yield select(state => state.detail.movieInfo) const attach = yield call(getMovieAttach, movieId) yield put(recieveMovieAttach(attach.data[0])) } } //组件中 componentWillUpdate(nextProps) { if (nextProps.loginStatus && (nextProps.movieInfo!==this.props.movieInfo)) { //是登录状态,并且movieInfo已经返回时 const { id } = this.props.match.params this.props.loadMovieAttach(id) } }
总结,合理使用组件的钩子函数,generator中不要处理太多操作,增加灵活性
后端后端采用express和mongodb,也用到了redis,主要技术点有使用pm2来管理node应用及部署代码,mongodb中开启身份认证,使用token+redis来做身份认证、在node中写了写单元测试,还是值得记录一下的
使用 jwt + redis 来做基于token的用户身份认证
基于token的认证流程
客户端发起登录请求
服务端验证用户名密码
验证成功服务端生成一个token,响应给客户端
客户端之后的每次请求header中都带上这个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") { return req.headers.authorization.split(" ")[1]; } else if (req.query && req.query.token) { return req.query.token; } return null; } }
为什么使用redis
**采用jsonwebtoken生成token时可以指定token的有效期,并且jsonwebtoken的verify方法也提供了选项来更新token的有效期,
但这里使用了express_jwt中间件,而express_jwt不提供方法来刷新token**
思路:
客户端请求登录成功,生成token
将此token保存在redis中,设置redis的有效期(例如1h)
新的请求过来,先express_jwt验证token,验证成功, 再验证token是否在redis中存在,存在说明有效
有效期内客户端新的请求过来,提取token,更新此token在redis中的有效期
客户端退出登录请求,删除redis中此token
具体代码
使用 mocha + supertest + should 来写单元测试
测试覆盖了所有接口,在开发中,因为没什么进度要求就慢慢写了,写完一个接口就去写一个测试,测试写也还算详细,等测试通过了再前端调接口,整个过程还是挺有意思的
mocha 是一个node单元测试框架,类似于前端的jasmine,语法也相近
supertest 用来测试node接口的库
should nodejs断言库,可读性很高
测试的一个例子,篇幅太长,就不放在这了
最后喜欢可以关注下,万一有福利呢。。。。。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/92233.html
摘要:本文以管理者的视角,与大家分享下我自年月入职小菜后,与前端同学一起是如何规划团队的技术栈的,这条技术栈上的技能点又是如何在不同童鞋不同业务中生长出来的。 Scott 近两年无论是面试还是线下线上的技术分享,遇到许许多多前端同学,由于团队原因,个人原因,职业成长,技术方向,甚至家庭等等原因,在理想国与现实之间,在放弃与坚守之间,摇摆不停,心酸硬抗,大家可以找我聊聊南聊聊北,对工程师的宿命...
摘要:尽量按照前端后端部署运维来讲,当然中途涉及到跨域这种前后协调的还是无法避免捎带一笔。关于我目前在写从零构建前后分离项目系列,修正和补充以此为准不断更新的项目实践地址彩蛋提前预览下一章传送门 序: 开源的意义 本系列提前首发地址 背景 从事了近4年的互联网行业,逐渐担当过团队的前端到后端的负责人,和大家一样从小白逐渐的成长起来,回首望去几年前的博客还是那么稚嫩。 回首这几年: 从一个ja...
摘要:从前端到后端到运维,经历了几次前后端架构的演变,踩了无数的坑,度过无数难免的夜。为了工作或学习,确实造过一些轮子,前端的后端的,也开源出来过觉得能提高生产力的。 showImg(https://segmentfault.com/img/bVbgeXP?w=713&h=275); 序: 开源的意义 本系列提前首发地址 背景 从事了近4年的互联网行业,逐渐担当过团队的前端到后端的负责人,和...
摘要:的网站仍然使用有漏洞库上周发布了开源社区安全现状报告,发现随着开源社区的日渐活跃,开源代码中包含的安全漏洞以及影响的范围也在不断扩大。与应用安全是流行的服务端框架,本文即是介绍如何使用以及其他的框架来增强应用的安全性。 showImg(https://segmentfault.com/img/remote/1460000012181337?w=1240&h=826); 前端每周清单专注...
摘要:实现不定期更新技巧前端掘金技巧,偶尔更新。统一播放效果实现打字效果动画前端掘金前端开源项目周报前端掘金由出品的前端开源项目周报第四期来啦。 Web 推送技术 - 掘金腾讯云技术社区-掘金主页持续为大家呈现云计算技术文章,欢迎大家关注! 作者:villainthr 摘自 前端小吉米 伴随着今年 Google I/O 大会的召开,一个很火的概念--Progressive Web Apps ...
阅读 1366·2021-09-23 11:21
阅读 2995·2019-08-30 14:14
阅读 3165·2019-08-30 13:56
阅读 3918·2019-08-30 11:20
阅读 1901·2019-08-29 17:23
阅读 2617·2019-08-29 16:14
阅读 1668·2019-08-28 18:18
阅读 1411·2019-08-26 12:14