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