摘要:其實(shí),該復(fù)雜的東西在哪放都復(fù)雜,只不過(guò)現(xiàn)在更清晰一點(diǎn)使用不好的地方就是太繁瑣了,定義各種各種組件。。。。。
之前做了個(gè)好電影搜集的小應(yīng)用,前端采用react,后端采用express+mongodb,最近又將組件間的狀態(tài)管理改成了redux,并加入了redux-saga來(lái)管理異步操作,記錄一些總結(jié)在線地址 手機(jī)模式
源碼
主要功能爬取豆瓣電影信息并錄入MongoDB
電影列表展示,分類(lèi)、搜索
電影詳情展示及附件管理
注冊(cè)、登錄
權(quán)限控制,普通用戶可以錄入、收藏,administrator錄入、修改、刪除
用戶中心,我的收藏列表
一些總結(jié) 前端前端使用了react,redux加redux-saga,對(duì)redux簡(jiǎn)單總結(jié)一下,同時(shí)記錄一個(gè)前后接口調(diào)用有依賴關(guān)系的問(wèn)題
redux
一句話總結(jié)redux,我覺(jué)的就是將組件之間的縱向的props傳遞和父子組件間的state愛(ài)恨糾纏給打平了,將一種縱向關(guān)系轉(zhuǎn)變成多個(gè)組件和一個(gè)獨(dú)立出來(lái)的狀態(tài)對(duì)象直接交互,這樣之后,代碼結(jié)構(gòu)確實(shí)看上去更加清晰了。
redux的核心概念,action,reducer,和store
action就是說(shuō)明我要操作一個(gè)狀態(tài)了,怎么操作是reducer的事,而所有狀態(tài)存儲(chǔ)在store中,store發(fā)出動(dòng)作并交由指定的reducer來(lái)處理
redux強(qiáng)制規(guī)范了我們對(duì)狀態(tài)的操作,只能在action和reducer這些東西中,這樣,原本錯(cuò)綜復(fù)雜的業(yè)務(wù)邏輯處理就換了個(gè)地,限制在了action和reducer中,組件看上去就很干凈了。其實(shí),該復(fù)雜的東西在哪放都復(fù)雜,只不過(guò)現(xiàn)在更清晰一點(diǎn)
使用redux不好的地方就是太繁瑣了,定義各種action,connect各種組件。。。?!,F(xiàn)在又出來(lái)一個(gè)Mobx,不明覺(jué)厲,反正大家都說(shuō)好~
redux-saga
redux-saga用來(lái)處理異步調(diào)用啥的,借助于generator,讓異步代碼看起來(lái)更簡(jiǎn)潔,常用的有take,takeLatest,takeEvery,put,call,fork,select,使用過(guò)程中遇到一個(gè)接口調(diào)用有前后依賴關(guān)系的問(wèn)題,比較有意思
描述一下:
有一個(gè)接口/api/user/checkLogin,用來(lái)判斷是否登錄,在最外層的
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) }
然后我有一個(gè)電影詳情頁(yè)組件,在這個(gè)組件的componentDidMount中會(huì)發(fā)起/api/movies/${id}接口獲取電影信息,如果用戶是登錄狀態(tài)的話,還會(huì)發(fā)起一個(gè)獲取電影附件信息的接口/api/movies/${id}/attach,整個(gè)步驟寫(xiě)在一個(gè)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) }
用戶登錄了,進(jìn)到詳情,流程正常,但如果在詳情頁(yè)刷新了頁(yè)面,獲取附件的接口沒(méi)觸發(fā),原因是此時(shí)checkLogin接口還沒(méi)返回結(jié)果,state.loginStatus狀態(tài)還是false,上面就沒(méi)走到if中
一開(kāi)始想著怎么控制一些generator中yield的先后順序來(lái)解決(如果用戶沒(méi)有登錄的話,再發(fā)一個(gè)CHECK_LOAGIN,結(jié)果返回了流程再繼續(xù)),但存在CHECK_LOAGIN調(diào)用兩次,如果登錄了,還會(huì)再多一次獲取用戶信息的接口調(diào)用的情況,肯定不行
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) { // //刷新頁(yè)面的時(shí)候,如果此時(shí)checklogin接口還沒(méi)返回?cái)?shù)據(jù)或還沒(méi)發(fā)出,應(yīng)觸發(fā)一個(gè)checklogin // //checklogin返回后才能得到login狀態(tài) // 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的職責(zé),componentWillUpdate中合適的觸發(fā)獲取附件的動(dòng)作
//將獲取附件的動(dòng)作從 getMovieInfo這個(gè)generator中分離出來(lái) 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)) { //是登錄狀態(tài),并且movieInfo已經(jīng)返回時(shí) const { id } = this.props.match.params this.props.loadMovieAttach(id) } }
總結(jié),合理使用組件的鉤子函數(shù),generator中不要處理太多操作,增加靈活性
后端后端采用express和mongodb,也用到了redis,主要技術(shù)點(diǎn)有使用pm2來(lái)管理node應(yīng)用及部署代碼,mongodb中開(kāi)啟身份認(rèn)證,使用token+redis來(lái)做身份認(rèn)證、在node中寫(xiě)了寫(xiě)單元測(cè)試,還是值得記錄一下的
使用 jwt + redis 來(lái)做基于token的用戶身份認(rèn)證
基于token的認(rèn)證流程
客戶端發(fā)起登錄請(qǐng)求
服務(wù)端驗(yàn)證用戶名密碼
驗(yàn)證成功服務(wù)端生成一個(gè)token,響應(yīng)給客戶端
客戶端之后的每次請(qǐng)求header中都帶上這個(gè)token
服務(wù)端對(duì)需要認(rèn)證的接口要驗(yàn)證token,驗(yàn)證成功接收請(qǐng)求
這里采用jsonwebtoken來(lái)生成token,
jwt.sign(payload, secretOrPrivateKey, [options, callback])
使用express-jwt驗(yàn)證token(驗(yàn)證成功會(huì)把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時(shí)可以指定token的有效期,并且jsonwebtoken的verify方法也提供了選項(xiàng)來(lái)更新token的有效期,
但這里使用了express_jwt中間件,而express_jwt不提供方法來(lái)刷新token**
思路:
客戶端請(qǐng)求登錄成功,生成token
將此token保存在redis中,設(shè)置redis的有效期(例如1h)
新的請(qǐng)求過(guò)來(lái),先express_jwt驗(yàn)證token,驗(yàn)證成功, 再驗(yàn)證token是否在redis中存在,存在說(shuō)明有效
有效期內(nèi)客戶端新的請(qǐng)求過(guò)來(lái),提取token,更新此token在redis中的有效期
客戶端退出登錄請(qǐng)求,刪除redis中此token
具體代碼
使用 mocha + supertest + should 來(lái)寫(xiě)單元測(cè)試
測(cè)試覆蓋了所有接口,在開(kāi)發(fā)中,因?yàn)闆](méi)什么進(jìn)度要求就慢慢寫(xiě)了,寫(xiě)完一個(gè)接口就去寫(xiě)一個(gè)測(cè)試,測(cè)試寫(xiě)也還算詳細(xì),等測(cè)試通過(guò)了再前端調(diào)接口,整個(gè)過(guò)程還是挺有意思的
mocha 是一個(gè)node單元測(cè)試框架,類(lèi)似于前端的jasmine,語(yǔ)法也相近
supertest 用來(lái)測(cè)試node接口的庫(kù)
should nodejs斷言庫(kù),可讀性很高
測(cè)試的一個(gè)例子,篇幅太長(zhǎng),就不放在這了
最后喜歡可以關(guān)注下,萬(wàn)一有福利呢。。。。。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/92233.html
摘要:本文以管理者的視角,與大家分享下我自年月入職小菜后,與前端同學(xué)一起是如何規(guī)劃團(tuán)隊(duì)的技術(shù)棧的,這條技術(shù)棧上的技能點(diǎn)又是如何在不同童鞋不同業(yè)務(wù)中生長(zhǎng)出來(lái)的。 Scott 近兩年無(wú)論是面試還是線下線上的技術(shù)分享,遇到許許多多前端同學(xué),由于團(tuán)隊(duì)原因,個(gè)人原因,職業(yè)成長(zhǎng),技術(shù)方向,甚至家庭等等原因,在理想國(guó)與現(xiàn)實(shí)之間,在放棄與堅(jiān)守之間,搖擺不停,心酸硬抗,大家可以找我聊聊南聊聊北,對(duì)工程師的宿命...
摘要:盡量按照前端后端部署運(yùn)維來(lái)講,當(dāng)然中途涉及到跨域這種前后協(xié)調(diào)的還是無(wú)法避免捎帶一筆。關(guān)于我目前在寫(xiě)從零構(gòu)建前后分離項(xiàng)目系列,修正和補(bǔ)充以此為準(zhǔn)不斷更新的項(xiàng)目實(shí)踐地址彩蛋提前預(yù)覽下一章傳送門(mén) 序: 開(kāi)源的意義 本系列提前首發(fā)地址 背景 從事了近4年的互聯(lián)網(wǎng)行業(yè),逐漸擔(dān)當(dāng)過(guò)團(tuán)隊(duì)的前端到后端的負(fù)責(zé)人,和大家一樣從小白逐漸的成長(zhǎng)起來(lái),回首望去幾年前的博客還是那么稚嫩。 回首這幾年: 從一個(gè)ja...
摘要:從前端到后端到運(yùn)維,經(jīng)歷了幾次前后端架構(gòu)的演變,踩了無(wú)數(shù)的坑,度過(guò)無(wú)數(shù)難免的夜。為了工作或?qū)W習(xí),確實(shí)造過(guò)一些輪子,前端的后端的,也開(kāi)源出來(lái)過(guò)覺(jué)得能提高生產(chǎn)力的。 showImg(https://segmentfault.com/img/bVbgeXP?w=713&h=275); 序: 開(kāi)源的意義 本系列提前首發(fā)地址 背景 從事了近4年的互聯(lián)網(wǎng)行業(yè),逐漸擔(dān)當(dāng)過(guò)團(tuán)隊(duì)的前端到后端的負(fù)責(zé)人,和...
摘要:的網(wǎng)站仍然使用有漏洞庫(kù)上周發(fā)布了開(kāi)源社區(qū)安全現(xiàn)狀報(bào)告,發(fā)現(xiàn)隨著開(kāi)源社區(qū)的日漸活躍,開(kāi)源代碼中包含的安全漏洞以及影響的范圍也在不斷擴(kuò)大。與應(yīng)用安全是流行的服務(wù)端框架,本文即是介紹如何使用以及其他的框架來(lái)增強(qiáng)應(yīng)用的安全性。 showImg(https://segmentfault.com/img/remote/1460000012181337?w=1240&h=826); 前端每周清單專(zhuān)注...
摘要:實(shí)現(xiàn)不定期更新技巧前端掘金技巧,偶爾更新。統(tǒng)一播放效果實(shí)現(xiàn)打字效果動(dòng)畫(huà)前端掘金前端開(kāi)源項(xiàng)目周報(bào)前端掘金由出品的前端開(kāi)源項(xiàng)目周報(bào)第四期來(lái)啦。 Web 推送技術(shù) - 掘金騰訊云技術(shù)社區(qū)-掘金主頁(yè)持續(xù)為大家呈現(xiàn)云計(jì)算技術(shù)文章,歡迎大家關(guān)注! 作者:villainthr 摘自 前端小吉米 伴隨著今年 Google I/O 大會(huì)的召開(kāi),一個(gè)很火的概念--Progressive Web Apps ...
閱讀 1492·2021-09-23 11:21
閱讀 3169·2019-08-30 14:14
閱讀 3246·2019-08-30 13:56
閱讀 4258·2019-08-30 11:20
閱讀 2016·2019-08-29 17:23
閱讀 2834·2019-08-29 16:14
閱讀 1760·2019-08-28 18:18
閱讀 1541·2019-08-26 12:14