>  기사  >  웹 프론트엔드  >  영화 컬렉션을 위한 소규모 애플리케이션의 React 기술 스택 실습

영화 컬렉션을 위한 소규모 애플리케이션의 React 기술 스택 실습

小云云
小云云원래의
2017-12-18 15:43:061426검색
이 글은 주로 영화 컬렉션을 위한 소규모 애플리케이션의 리액트 기술 스택 실습을 공유하는데, 도움이 되기를 바랍니다.

주요 기능

  • 두반 영화 정보를 크롤링하여 MongoDB에 입력

  • 영화 목록 표시, 분류, 검색

  • 영화 세부 정보 표시 및 첨부 파일 관리

  • 등록, 로그인

  • 권한 control, 일반 사용자는 입력, 수집, 관리자 입력, 수정, 삭제 가능

  • 사용자 센터, 즐겨찾기 목록

영화 컬렉션을 위한 소규모 애플리케이션의 React 기술 스택 실습

일부 요약

front-end

프론트 엔드는 React를 사용합니다. redux plus redux- saga, redux에 대한 간략한 요약, 전면 인터페이스 호출과 후면 인터페이스 호출 간의 종속성 문제 기록

  • redux

redux를 한 문장으로 요약하자면 컴포넌트와 컴포넌트 사이의 수직 prop을 옮기는 것이라고 생각합니다. 상위 및 하위 구성 요소 간 상태의 애증 얽힘이 완화되었으며 수직 관계가 독립적인 상태 개체와 직접 상호 작용하는 여러 구성 요소로 변환되었습니다. 더 명확해졌습니다. 多个组件和一个独立出来的状态对象直接交互,这样之后,代码结构确实看上去更加清晰了。

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는 액션과 리듀서에서만 수행할 수 있는 작업을 상태에서 표준화하도록 강제합니다. 이러한 방식으로 원래 복잡했던 비즈니스 로직 처리가 변경되고 액션과 리듀서로 제한되어 있어 컴포넌트가 매우 깨끗해 보입니다. 사실 이 복잡한 것을 어디에 넣어도 복잡하지만 이제는 조금 더 명확해졌습니다
redux를 사용하는 단점은 다양한 액션을 정의하고 다양한 컴포넌트를 연결하는 것이 너무 번거롭다는 것입니다. . . . . 이제 또 다른 Mobx가 있는데 그것이 얼마나 강력한지는 모르겠지만 모두가 동의합니다~

  1. redux-saga

  2. redux-saga는 생성기의 도움으로 비동기 호출 등을 처리하는 데 사용됩니다. 비동기 코드가 더 간결해 보입니다. 일반적으로 사용되는 코드는 take, takeLatest, takeEvery, put, call, fork, select입니다. 사용 중에 인터페이스 호출의 종속성에 문제가 발생했습니다. 설명해보세요:

  3. 로그인 여부를 결정하는 데 사용되는 /api/user/checkLogin 인터페이스가 있습니다. 작업은 가장 바깥쪽 이 요청을 시작하기 위한 구성 요소와 반환 상태가 로그인된 경우 사용자 정보를 얻기 위해

  4. jwt.sign(payload, secretOrPrivateKey, [options, callback])
  5. 을 전송해야 하며 comComponentDidMount >/api/movies/${id} 인터페이스에서 시작됩니다. 에서는 영화 첨부 정보를 얻기 위한 인터페이스도 /api/movies /${id}/attach로 시작됩니다. 전체 단계는 생성기에 기록됩니다.
  6. 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;
        }
        }
사용자가 로그인하여 세부정보를 입력했는데, 세부정보 페이지에서 페이지를 새로고침했는데 첨부파일을 가져오는 인터페이스가 실행되지 않았습니다. checkLogin 인터페이스가 현재 결과를 반환하지 않았다는 것입니다. state.loginStatus 상태는 여전히 false이고 위의 내용은 if

로 이동하지 않습니다. 처음에는 어떻게 해야할지 생각했습니다. 문제를 해결하기 위해 일부 생성기의 수율 순서를 제어하지만(사용자가 로그인하지 않은 경우 다른 CHECK_LOAGIN을 보내면 프로세스가 계속 진행됨) 사용자가 로그인한 경우 CHECK_LOAGIN에 대한 두 번의 호출이 있습니다. 사용자 정보를 한 번에 얻기 위해 인터페이스를 호출하는 것은 확실히 불가능합니다

🎜rrreee🎜🎜🎜마지막 방법은 생성자의 책임을 분해하고 컴포넌트WillUpdate에서 첨부 파일을 얻는 작업을 적절하게 트리거하는 것입니다🎜🎜 🎜rrreee🎜🎜🎜결론적으로 컴포넌트를 합리적으로 사용 Hook 기능, 생성기에서 너무 많은 연산을 처리하지 않고 유연성을 높인다🎜🎜🎜🎜Backend🎜🎜 백엔드는 express와 mongodb를 사용하며, redis도 사용하는 것이 주요 기술 포인트입니다. pm2를 사용하여 노드 애플리케이션 관리 및 코드 배포, mongodb에서 신원 인증 활성화, 신원 인증을 위해 토큰+redis 사용, 노드에서 단위 테스트 작성, 기록할 가치가 있습니다🎜🎜🎜🎜jwt + redis 사용 토큰 기반 사용자 신원 인증을 위해 🎜🎜🎜🎜토큰 기반 인증 프로세스🎜🎜🎜🎜클라이언트가 로그인 요청을 시작합니다🎜🎜🎜🎜서버가 사용자 이름과 비밀번호를 확인합니다🎜🎜🎜🎜확인에 성공하면 서버는 다음을 생성합니다. 토큰을 생성하고 클라이언트🎜🎜🎜🎜Client에 응답합니다. 각 후속 요청 헤더는 이 토큰을 가져옵니다🎜🎜🎜🎜서버는 인증이 필요한 인터페이스에 대한 토큰을 확인해야 하며, 확인을 통해 요청이 성공적으로 수신됩니다🎜🎜🎜🎜여기 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를 사용하여 단위 테스트를 작성합니다

  • 테스트는 모든 인터페이스를 대상으로 합니다. 개발 중에는 진행 요구 사항이 없기 때문에 인터페이스 하나만 작성하고 테스트를 완료하면 프런트 엔드에서 인터페이스를 조정합니다. . 전체 과정이 꽤 흥미롭습니다

mocha는 프론트엔드 Jasmine과 비슷한 노드 단위 테스트 프레임워크이고 구문도 비슷합니다

supertest는 노드 인터페이스를 테스트하는 데 사용되는 라이브러리입니다

읽기 쉬운 nodejs 어설션 라이브러리

테스트의 예, 너무 길기 때문에 여기에 넣지 않겠습니다관련 권장 사항:

React의 내부 메커니즘 탐색

방법은 무엇입니까? React에서 컴포넌트를 작성하려면?

위 내용은 영화 컬렉션을 위한 소규모 애플리케이션의 React 기술 스택 실습의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.