この記事では主に、ムービーコレクション用の小さなアプリケーションの反応技術スタックの実践について説明します。お役に立てれば幸いです。
Doubanの動画情報をクロールしてMongoDBに入力
動画一覧表示、分類、検索
動画詳細表示と添付ファイル管理
登録、ログイン
ミッションcontrol 、一般ユーザーは入力、収集、管理者入力、変更、削除ができます
ユーザーセンター、お気に入りリスト
フロントエンドはreactを使用します、 redux と redux- saga、redux の簡単な概要、フロントインターフェイス呼び出しとリアインターフェイス呼び出しの間の依存関係の問題を記録します
redux を一文で要約すると、コンポーネントとコンポーネントの間で垂直プロパティを転送することだと思います。親コンポーネントと子コンポーネントの間の状態の愛憎のもつれが解消され、垂直関係が 独立した状態オブジェクトと直接対話する複数のコンポーネント
に変換されました。この後のコード構造は次のようになります。より明確に。 多个组件和一个独立出来的状态对象直接交互
,这样之后,代码结构确实看上去更加清晰了。
redux的核心概念,action,reducer,和store
action就是说明我要操作一个状态了,怎么操作是reducer的事,而所有状态存储在store中,store发出动作并交由指定的reducer来处理
redux强制规范了我们对状态的操作,只能在action和reducer这些东西中,这样,原本错综复杂的业务逻辑处理就换了个地,限制在了action和reducer中,组件看上去就很干净了。其实,该复杂的东西在哪放都复杂,只不过现在更清晰一点
使用redux不好的地方就是太繁琐了,定义各种action,connect各种组件。。。。。现在又出来一个Mobx,不明觉厉,反正大家都说好~
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应用及部署代码
action は、状態を操作することを意味します。操作方法は、reducer の仕事であり、すべての状態はストアに保存されます。
redux は、アクションとリデューサーでしか実行できないステートに対する操作を標準化することを強制します。このようにして、元々複雑なビジネス ロジックの処理が変更されます。アクションとリデューサーに限定されているため、コンポーネントは非常にきれいに見えます。実際、この複雑なことをどこに配置しても複雑ですが、これで少し明確になりましたtake、takelatest、takeEvery、put、call、fork、select
です。使用中に、インターフェイス呼び出しの依存関係に問題が発生しました。興味深いです。説明してください:
/api/movies /${id}/attach
。ステップ全体はジェネレーターで記述されます
jwt.sign(payload, secretOrPrivateKey, [options, callback])
state.loginStatus
のステータスはまだ false であり、上記は if に進みません
最初はどうすればよいかを考えていました。問題を解決するために、一部のジェネレーターで生成のシーケンスを制御します (ユーザーがログインしていない場合は、別の CHECK_LOAGIN を送信すると、プロセスが戻って続行されます)。ただし、ユーザーがログインしている場合は、CHECK_LOAGIN への呼び出しが 2 回あります。ユーザー情報を取得するためにインターフェイスを一度に呼び出すことは絶対に不可能です🎜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; } }
pm2 を使用してノード アプリケーションを管理し、コードをデプロイします
、mongodb で ID 認証を有効にし、ID 認証にトークン + Redis を使用し、ノードで単体テストを作成します。これは記録する価値があります🎜🎜🎜🎜 jwt + redis を使用するトークンベースのユーザー ID 認証の場合🎜🎜🎜🎜トークンベースの認証プロセス🎜🎜🎜🎜クライアントはログイン要求を開始します🎜🎜🎜🎜サーバーはユーザー名とパスワードを検証します🎜🎜🎜🎜検証が成功すると、サーバーはトークンを取得してクライアントに応答します🎜🎜🎜🎜クライアント後続の各リクエストヘッダーはこのトークンをもたらします🎜🎜🎜🎜サーバーは認証を必要とするインターフェースのトークンを検証する必要があり、検証によりリクエストが正常に受信されます🎜🎜🎜🎜ここjsonwebtoken はトークンの生成に使用されます。🎜rrreee🎜検証トークンには Express-jwt を使用します (検証が成功すると、トークン情報が request.user に書き込まれます)🎜rrreee🎜 redis を使用する理由🎜** jsonwebtoken を使用してトークンを生成する場合、トークンの有効期間を指定できます。jsonwebtoken の verify メソッドには、トークンの有効期間を更新するオプションも用意されています。
ただし、ここでは Express_jwt ミドルウェアが使用されており、express_jwt がそれを行います。トークンを更新するメソッドを提供しない**
アイデア:
クライアントは正常にログインを要求し、トークンを生成します
このトークンをredisに保存し、redisの有効期間(たとえば、1時間)を設定します
新しいリクエストが来ると、最初にexpress_jwtがトークンを検証し、検証が成功した後、トークンがredisに存在するかどうかを検証します
supertestはノードインターフェイスをテストするために使用されるライブラリです
nodejs アサーション ライブラリ、非常に読みやすいです
テストの例ですが、長すぎるのでここには載せません関連する推奨事項:
React の内部メカニズムを探索する
方法は何ですかReact でコンポーネントを書くには?
以上が動画コレクション用の小さなアプリケーションの React テクノロジー スタックの実践の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。