<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [上一篇文章](216731),我介紹了 Redux 的基本做法:用戶發出 Action,Reducer 函數算出新的 State,View 重新渲染。 ![](http://www.ruanyifeng.com/blogimg/asset/2016/bg2016092001.jpg) 但是,一個關鍵問題沒有解決:異步操作怎么辦?Action 發出以后,Reducer 立即算出 State,這叫做同步;Action 發出以后,過一段時間再執行 Reducer,這就是異步。 怎么才能 Reducer 在異步操作結束后自動執行呢?這就要用到新的工具:中間件(middleware)。 ![](http://www.ruanyifeng.com/blogimg/asset/2016/bg2016092002.jpg) 主要內容: [TOC] ## 一、中間件的概念 為了理解中間件,讓我們站在框架作者的角度思考問題:如果要添加功能,你會在哪個環節添加? > (1)Reducer:純函數,只承擔計算 State 的功能,不合適承擔其他功能,也承擔不了,因為理論上,純函數不能進行讀寫操作。 > > (2)View:與 State 一一對應,可以看作 State 的視覺層,也不合適承擔其他功能。 > > (3)Action:存放數據的對象,即消息的載體,只能被別人操作,自己不能進行任何操作。 想來想去,只有發送 Action 的這個步驟,即`store.dispatch()`方法,可以添加功能。舉例來說,要添加日志功能,把 Action 和 State 打印出來,可以對`store.dispatch`進行如下改造。 > ~~~ > let next = store.dispatch; > store.dispatch = function dispatchAndLog(action) { > console.log('dispatching', action); > next(action); > console.log('next state', store.getState()); > } > ~~~ 上面代碼中,對`store.dispatch`進行了重定義,在發送 Action 前后添加了打印功能。這就是中間件的雛形。 中間件就是一個函數,對`store.dispatch`方法進行了改造,在發出 Action 和執行 Reducer 這兩步之間,添加了其他功能。 ## 二、中間件的用法 本教程不涉及如何編寫中間件,因為常用的中間件都有現成的,只要引用別人寫好的模塊即可。比如,上一節的日志中間件,就有現成的[redux-logger](https://github.com/evgenyrodionov/redux-logger)模塊。這里只介紹怎么使用中間件。 > ~~~ > import { applyMiddleware, createStore } from 'redux'; > import createLogger from 'redux-logger'; > const logger = createLogger(); > > const store = createStore( > reducer, > applyMiddleware(logger) > ); > ~~~ 上面代碼中,`redux-logger`提供一個生成器`createLogger`,可以生成日志中間件`logger`。然后,將它放在`applyMiddleware`方法之中,傳入`createStore`方法,就完成了`store.dispatch()`的功能增強。 這里有兩點需要注意: (1)`createStore`方法可以接受整個應用的初始狀態作為參數,那樣的話,`applyMiddleware`就是第三個參數了。 > ~~~ > const store = createStore( > reducer, > initial_state, > applyMiddleware(logger) > ); > ~~~ (2)中間件的次序有講究。 > ~~~ > const store = createStore( > reducer, > applyMiddleware(thunk, promise, logger) > ); > ~~~ 上面代碼中,`applyMiddleware`方法的三個參數,就是三個中間件。有的中間件有次序要求,使用前要查一下文檔。比如,`logger`就一定要放在最后,否則輸出結果會不正確。 ## 三、applyMiddlewares() 看到這里,你可能會問,`applyMiddlewares`這個方法到底是干什么的? 它是 Redux 的原生方法,作用是將所有中間件組成一個數組,依次執行。下面是它的源碼。 > ~~~ > export default function applyMiddleware(...middlewares) { > return (createStore) => (reducer, preloadedState, enhancer) => { > var store = createStore(reducer, preloadedState, enhancer); > var dispatch = store.dispatch; > var chain = []; > > var middlewareAPI = { > getState: store.getState, > dispatch: (action) => dispatch(action) > }; > chain = middlewares.map(middleware => middleware(middlewareAPI)); > dispatch = compose(...chain)(store.dispatch); > > return {...store, dispatch} > } > } > ~~~ 上面代碼中,所有中間件被放進了一個數組`chain`,然后嵌套執行,最后執行`store.dispatch`。可以看到,中間件內部(`middlewareAPI`)可以拿到`getState`和`dispatch`這兩個方法。 ## 四、異步操作的基本思路 理解了中間件以后,就可以處理異步操作了。 同步操作只要發出一種 Action 即可,異步操作的差別是它要發出三種 Action。 > * 操作發起時的 Action > * 操作成功時的 Action > * 操作失敗時的 Action 以向服務器取出數據為例,三種 Action 可以有兩種不同的寫法。 > ~~~ > // 寫法一:名稱相同,參數不同 > { type: 'FETCH_POSTS' } > { type: 'FETCH_POSTS', status: 'error', error: 'Oops' } > { type: 'FETCH_POSTS', status: 'success', response: { ... } } > > // 寫法二:名稱不同 > { type: 'FETCH_POSTS_REQUEST' } > { type: 'FETCH_POSTS_FAILURE', error: 'Oops' } > { type: 'FETCH_POSTS_SUCCESS', response: { ... } } > ~~~ 除了 Action 種類不同,異步操作的 State 也要進行改造,反映不同的操作狀態。下面是 State 的一個例子。 > ~~~ > let state = { > // ... > isFetching: true, > didInvalidate: true, > lastUpdated: 'xxxxxxx' > }; > ~~~ 上面代碼中,State 的屬性`isFetching`表示是否在抓取數據。`didInvalidate`表示數據是否過時,`lastUpdated`表示上一次更新時間。 現在,整個異步操作的思路就很清楚了。 > * 操作開始時,送出一個 Action,觸發 State 更新為"正在操作"狀態,View 重新渲染 > * 操作結束后,再送出一個 Action,觸發 State 更新為"操作結束"狀態,View 再一次重新渲染 ## 五、redux-thunk 中間件 異步操作至少要送出兩個 Action:用戶觸發第一個 Action,這個跟同步操作一樣,沒有問題;如何才能在操作結束時,系統自動送出第二個 Action 呢? 奧妙就在 Action Creator 之中。 > ~~~ > class AsyncApp extends Component { > componentDidMount() { > const { dispatch, selectedPost } = this.props > dispatch(fetchPosts(selectedPost)) > } > > // ... > ~~~ 上面代碼是一個異步組件的例子。加載成功后(`componentDidMount`方法),它送出了(`dispatch`方法)一個 Action,向服務器要求數據?`fetchPosts(selectedSubreddit)`。這里的`fetchPosts`就是 Action Creator。 下面就是`fetchPosts`的代碼,關鍵之處就在里面。 ![](http://www.ruanyifeng.com/blogimg/asset/2016/bg2016092003.jpg) > ~~~ > const fetchPosts = postTitle => (dispatch, getState) => { > dispatch(requestPosts(postTitle)); > return fetch(`/some/API/${postTitle}.json`) > .then(response => response.json()) > .then(json => dispatch(receivePosts(postTitle, json))); > }; > }; > > // 使用方法一 > store.dispatch(fetchPosts('reactjs')); > // 使用方法二 > store.dispatch(fetchPosts('reactjs')).then(() => > console.log(store.getState()) > ); > ~~~ 上面代碼中,`fetchPosts`是一個Action Creator(動作生成器),返回一個函數。這個函數執行后,先發出一個Action(`requestPosts(postTitle)`),然后進行異步操作。拿到結果后,先將結果轉成 JSON 格式,然后再發出一個 Action(?`receivePosts(postTitle, json)`)。 上面代碼中,有幾個地方需要注意。 > (1)`fetchPosts`返回了一個函數,而普通的 Action Creator 默認返回一個對象。 > > (2)返回的函數的參數是`dispatch`和`getState`這兩個 Redux 方法,普通的 Action Creator 的參數是 Action 的內容。 > > (3)在返回的函數之中,先發出一個 Action(`requestPosts(postTitle)`),表示操作開始。 > > (4)異步操作結束之后,再發出一個 Action(`receivePosts(postTitle, json)`),表示操作結束。 這樣的處理,就解決了自動發送第二個 Action 的問題。但是,又帶來了一個新的問題,Action 是由`store.dispatch`方法發送的。而`store.dispatch`方法正常情況下,參數只能是對象,不能是函數。 這時,就要使用中間件[`redux-thunk`](https://github.com/gaearon/redux-thunk)。 > ~~~ > import { createStore, applyMiddleware } from 'redux'; > import thunk from 'redux-thunk'; > import reducer from './reducers'; > > // Note: this API requires redux@>=3.1.0 > const store = createStore( > reducer, > applyMiddleware(thunk) > ); > ~~~ 上面代碼使用`redux-thunk`中間件,改造`store.dispatch`,使得后者可以接受函數作為參數。 因此,異步操作的第一種解決方案就是,寫出一個返回函數的 Action Creator,然后使用`redux-thunk`中間件改造`store.dispatch`。 ## 六、redux-promise 中間件 既然 Action Creator 可以返回函數,當然也可以返回其他值。另一種異步操作的解決方案,就是讓 Action Creator 返回一個 Promise 對象。 這就需要使用`redux-promise`中間件。 > ~~~ > import { createStore, applyMiddleware } from 'redux'; > import promiseMiddleware from 'redux-promise'; > import reducer from './reducers'; > > const store = createStore( > reducer, > applyMiddleware(promiseMiddleware) > ); > ~~~ 這個中間件使得`store.dispatch`方法可以接受 Promise 對象作為參數。這時,Action Creator 有兩種寫法。寫法一,返回值是一個 Promise 對象。 > ~~~ > const fetchPosts = > (dispatch, postTitle) => new Promise(function (resolve, reject) { > dispatch(requestPosts(postTitle)); > return fetch(`/some/API/${postTitle}.json`) > .then(response => { > type: 'FETCH_POSTS', > payload: response.json() > }); > }); > ~~~ 寫法二,Action 對象的`payload`屬性是一個 Promise 對象。這需要從[`redux-actions`](https://github.com/acdlite/redux-actions)模塊引入`createAction`方法,并且寫法也要變成下面這樣。 > ~~~ > import { createAction } from 'redux-actions'; > > class AsyncApp extends Component { > componentDidMount() { > const { dispatch, selectedPost } = this.props > // 發出同步 Action > dispatch(requestPosts(selectedPost)); > // 發出異步 Action > dispatch(createAction( > 'FETCH_POSTS', > fetch(`/some/API/${postTitle}.json`) > .then(response => response.json()) > )); > } > ~~~ 上面代碼中,第二個`dispatch`方法發出的是異步 Action,只有等到操作結束,這個 Action 才會實際發出。注意,`createAction`的第二個參數必須是一個 Promise 對象。 看一下`redux-promise`的[源碼](https://github.com/acdlite/redux-promise/blob/master/src/index.js),就會明白它內部是怎么操作的。 > ~~~ > export default function promiseMiddleware({ dispatch }) { > return next => action => { > if (!isFSA(action)) { > return isPromise(action) > ? action.then(dispatch) > : next(action); > } > > return isPromise(action.payload) > ? action.payload.then( > result => dispatch({ ...action, payload: result }), > error => { > dispatch({ ...action, payload: error, error: true }); > return Promise.reject(error); > } > ) > : next(action); > }; > } > ~~~ 從上面代碼可以看出,如果 Action 本身是一個 Promise,它 resolve 以后的值應該是一個 Action 對象,會被`dispatch`方法送出(`action.then(dispatch)`),但 reject 以后不會有任何動作;如果 Action 對象的`payload`屬性是一個 Promise 對象,那么無論 resolve 和 reject,`dispatch`方法都會發出 Action。 中間件和異步操作,就介紹到這里。[下一篇文章](216733)將是最后一部分,介紹如何使用`react-redux`這個庫。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看