摘要:其實,該復雜的東西在哪放都復雜,只不過現在更清晰一點使用不好的地方就是太繁瑣了,定義各種各種組件。。。。。
之前做了個好電影搜集的小應用,前端采用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斷言庫,可讀性很高
測試的一個例子,篇幅太長,就不放在這了
最后喜歡可以關注下,萬一有福利呢。。。。。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/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 ...
閱讀 1408·2021-09-23 11:21
閱讀 3104·2019-08-30 14:14
閱讀 3187·2019-08-30 13:56
閱讀 4135·2019-08-30 11:20
閱讀 1949·2019-08-29 17:23
閱讀 2764·2019-08-29 16:14
閱讀 1693·2019-08-28 18:18
閱讀 1490·2019-08-26 12:14