ホームページ  >  記事  >  ウェブフロントエンド  >  動画コレクション用の小さなアプリケーションの React テクノロジー スタックの実践

動画コレクション用の小さなアプリケーションの React テクノロジー スタックの実践

小云云
小云云オリジナル
2017-12-18 15:43:061426ブラウズ
この記事では主に、ムービーコレクション用の小さなアプリケーションの反応技術スタックの実践について説明します。お役に立てれば幸いです。

主な機能

  • Doubanの動画情報をクロールしてMongoDBに入力

  • 動画一覧表示、分類、検索

  • 動画詳細表示と添付ファイル管理

  • 登録、ログイン

  • ミッションcontrol 、一般ユーザーは入力、収集、管理者入力、変更、削除ができます

  • ユーザーセンター、お気に入りリスト

動画コレクション用の小さなアプリケーションの React テクノロジー スタックの実践

いくつかの概要

フロントエンド

フロントエンドはreactを使用します、 redux と redux- saga、redux の簡単な概要、フロントインターフェイス呼び出しとリアインターフェイス呼び出しの間の依存関係の問題を記録します

  • redux

redux を一文で要約すると、コンポーネントとコンポーネントの間で垂直プロパティを転送することだと思います。親コンポーネントと子コンポーネントの間の状態の愛憎のもつれが解消され、垂直関係が 独立した状態オブジェクトと直接対話する複数のコンポーネント に変換されました。この後のコード構造は次のようになります。より明確に。 多个组件和一个独立出来的状态对象直接交互,这样之后,代码结构确实看上去更加清晰了。

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,使用过程中遇到一个接口调用有前后依赖关系的问题,比较有意思

描述一下:

  1. 有一个接口/api/user/checkLogin,用来判断是否登录,在最外层的组件的componentDidMount中触发action来发起这个请求,并且接口返回状态是登录的话,还要发一个获取用户信息的

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)
}
  1. 然后我有一个电影详情页组件,在这个组件的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)
}
  1. 用户登录了,进到详情,流程正常,但如果在详情页刷新了页面,获取附件的接口没触发,原因是此时checkLogin接口还没返回结果,state.loginStatus状态还是false,上面就没走到if中

  2. 一开始想着怎么控制一些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]))
    }
}
  1. 最终的办法,分解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)
        }
}
  1. 总结,合理使用组件的钩子函数,generator中不要处理太多操作,增加灵活性

后端

后端采用express和mongodb,也用到了redis,主要技术点有使用pm2来管理node应用及部署代码

redux、action、reducer、store の中心的な概念
  • action は、状態を操作することを意味します。操作方法は、reducer の仕事であり、すべての状態はストアに保存されます。

    redux は、アクションとリデューサーでしか実行できないステートに対する操作を標準化することを強制します。このようにして、元々複雑なビジネス ロジックの処理が変更されます。アクションとリデューサーに限定されているため、コンポーネントは非常にきれいに見えます。実際、この複雑なことをどこに配置しても複雑ですが、これで少し明確になりました
redux を使用する欠点は、さまざまなアクションを定義し、さまざまなコンポーネントを接続するのが面倒すぎることです。 。 。 。 。今、別のMobxがあります。それがどれほど強力であるかはわかりませんが、誰もが同意します~

  1. redux-saga

  2. redux-sagaは、ジェネレーターの助けを借りて、非同期呼び出しなどを処理するために使用されます。一般的に使用される非同期コードは、take、takelatest、takeEvery、put、call、fork、select です。使用中に、インターフェイス呼び出しの依存関係に問題が発生しました。興味深いです。説明してください:

  3. ログインするかどうかを決定するために使用されるインターフェース /api/user/checkLogin があります。このリクエストを開始するための コンポーネントと、戻りステータスがログインしている場合は、ユーザー情報を取得するために

  4. rreee
  5. を送信する必要があります。そして、componentDidMount >/api/movies/${id} インターフェイスでコードが開始されます。では、映画の添付情報を取得するインターフェイスも開始されます /api/movies /${id}/attachステップ全体はジェネレーターで記述されます
  6. 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;
    }
    }
🎜🎜🎜最後の方法は、ジェネレーターの役割を分解し、componentWillUpdateで添付ファイルを取得するアクションを適切にトリガーすることです🎜🎜 🎜rrreee🎜🎜🎜 まとめると、 コンポーネントを合理的に使う 関数をフックする、ジェネレーターであまり多くの操作を処理しない、柔軟性を高める🎜🎜🎜🎜バックエンド🎜🎜 バックエンドはexpressとmongodbを使用し、redisも使用する が主な技術的なポイントです。 pm2 を使用してノード アプリケーションを管理し、コードをデプロイします 、mongodb で ID 認証を有効にし、ID 認証にトークン + Redis を使用し、ノードで単体テストを作成します。これは記録する価値があります🎜🎜🎜🎜 jwt + redis を使用するトークンベースのユーザー ID 認証の場合🎜🎜🎜🎜トークンベースの認証プロセス🎜🎜🎜🎜クライアントはログイン要求を開始します🎜🎜🎜🎜サーバーはユーザー名とパスワードを検証します🎜🎜🎜🎜検証が成功すると、サーバーはトークンを取得してクライアントに応答します🎜🎜🎜🎜クライアント後続の各リクエストヘッダーはこのトークンをもたらします🎜🎜🎜🎜サーバーは認証を必要とするインターフェースのトークンを検証する必要があり、検証によりリクエストが正常に受信されます🎜🎜🎜🎜ここjsonwebtoken はトークンの生成に使用されます。🎜rrreee🎜検証トークンには Express-jwt を使用します (検証が成功すると、トークン情報が request.user に書き込まれます)🎜rrreee🎜 redis を使用する理由🎜

** jsonwebtoken を使用してトークンを生成する場合、トークンの有効期間を指定できます。jsonwebtoken の verify メソッドには、トークンの有効期間を更新するオプションも用意されています。
ただし、ここでは Express_jwt ミドルウェアが使用されており、express_jwt がそれを行います。トークンを更新するメソッドを提供しない**

アイデア:

  1. クライアントは正常にログインを要求し、トークンを生成します

  2. このトークンをredisに保存し、redisの有効期間(たとえば、1時間)を設定します

  3. 新しいリクエストが来ると、最初にexpress_jwtがトークンを検証し、検証が成功した後、トークンがredisに存在するかどうかを検証します

  4. クライアントは有効期間中に新しいリクエストを作成し、抽出します。トークンを取得し、redis でこのトークンの有効期間を更新します

  5. クライアントはログイン要求を終了し、redis で削除します このトークンの特定のコード

mocha + supertest + should を使用して単体テストを作成します

  • テストはすべてのインターフェイスをカバーします。開発中は、進捗要件がないため、テストを 1 つだけ書きます。テストが完了すると、フロントエンドがインターフェイスを調整します。プロセス全体は非常に興味深いものです

  • mochaはフロントエンドjasmineに似たノードユニットテストフレームワークであり、構文も似ています

supertestはノードインターフェイスをテストするために使用されるライブラリです

nodejs アサーション ライブラリ、非常に読みやすいです

テストの例ですが、長すぎるのでここには載せません

関連する推奨事項:

React の内部メカニズムを探索する

方法は何ですかReact でコンポーネントを書くには?

以上が動画コレクション用の小さなアプリケーションの React テクノロジー スタックの実践の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。