摘要:中間件對(duì)異步的實(shí)現(xiàn)非常重要,因?yàn)樵谥暗奈恼轮形覀冋劦?,是一個(gè)行為抽象,只是一個(gè)對(duì)象,是一個(gè)純函數(shù),不應(yīng)該有調(diào)用和副作用的操作。這個(gè)函數(shù)并不需要保持純凈,它還可以帶有副作用,包括執(zhí)行異步請(qǐng)求。那么如何在中進(jìn)行網(wǎng)絡(luò)請(qǐng)求標(biāo)準(zhǔn)的做法是使用。
在之前的淺談Flux架構(gòu)及Redux實(shí)踐一文中我們初步的談及了Redux的數(shù)據(jù)流思想,并做了一個(gè)簡(jiǎn)單的加減器。但是還沒有接觸到Redux更多常用的場(chǎng)景,異步操作、API調(diào)用,如何連接到UI層等,Redux可以與很多框架搭配包括Vue、React甚至是純JavaScript。后面我們會(huì)用一個(gè)實(shí)例--通過github API獲取個(gè)人信息,來(lái)將Redux middleware、async action、連接到React貫穿其中。先看看我們最后寫的demo的樣子。
Middleware與異步Action依然先看看Redux作者Dam的描述:
It provides a third-party extension point between dispatching an
action, and the moment it reaches the reducer.
我的理解是,middleware提供了一個(gè)你可以修改action的機(jī)制,這和Express/Koa的中間件有些類似,只不過這里的中間件主要是操作action。中間件對(duì)異步的action實(shí)現(xiàn)非常重要,因?yàn)樵谥暗奈恼轮形覀冋劦剑琣ction是一個(gè)行為抽象,只是一個(gè)對(duì)象,reducer是一個(gè)純函數(shù),不應(yīng)該有API調(diào)用和副作用的操作。那么怎么解決異步的問題?我們肯定不能在reducer中寫,那么就考慮到了action -> reducer這個(gè)過程,這就是redux middleware:
action -> middleware modify action -> reducer
它提供的是位于 action 被發(fā)起之后,到達(dá) reducer 之前的擴(kuò)展點(diǎn)。 你可以利用 Redux middleware 來(lái)進(jìn)行日志記錄、創(chuàng)建崩潰報(bào)告、調(diào)用異步接口或者路由等等。
在上一篇文章中我們使用的同步action,action creator返回的是一個(gè)對(duì)象,但是異步action可以是一個(gè)函數(shù),雖然函數(shù)也是對(duì)象,這里我們只是為了區(qū)分兩種不同的情況。通過使用指定的 middleware,action creator可以返回函數(shù)。這時(shí),這個(gè) action creator 就成為了 thunk。當(dāng) action creator 返回函數(shù)時(shí),這個(gè)函數(shù)會(huì)被 Redux Thunk middleware 執(zhí)行。這個(gè)函數(shù)并不需要保持純凈,它還可以帶有副作用,包括執(zhí)行異步 API 請(qǐng)求。這個(gè)函數(shù)還可以 dispatch action,就像 dispatch 前面定義的同步 action 一樣。那么如何在action中進(jìn)行網(wǎng)絡(luò)請(qǐng)求?標(biāo)準(zhǔn)的做法是使用 Redux Thunk middleware。要引入 redux-thunk 這個(gè)專門的庫(kù)才能使用。
搭建工作流我們將采用ES6語(yǔ)法,webpack進(jìn)行打包,webpack-dev-server啟一個(gè)本地服務(wù)器,然后用HMR技術(shù)進(jìn)行React熱加載,看看webpack配置信息:
var webpack = require("webpack"); var OpenBrowserPlugin = require("open-browser-webpack-plugin"); module.exports = { entry: { index: [ "webpack/hot/dev-server", "webpack-dev-server/client?http://localhost:8080", "./src/index.js", ] }, output: { path: "./build", filename: "[name].js", }, devtool: "source-map", module: { loaders: [{ test: /.js$/, loader: "babel", query: { presets: ["es2015", "stage-0", "react"], }, }, { test: /.less$/, loader: "style!css!less", }], }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), new OpenBrowserPlugin({ url: "http://localhost:8080" }), ] };
其中open-browser-webpack-plugin插件將會(huì)幫助我們自動(dòng)打開瀏覽器,用babel進(jìn)行es編譯,less來(lái)維護(hù)我們的css樣式,以及使用dev-tool來(lái)生成source map,HotModuleReplacementPlugin來(lái)進(jìn)行熱更新。
再看看我們最后的目錄結(jié)構(gòu):
├── build │?? ├── index.html │?? └── index.js ├── node_modules ├── package.json ├── src │?? ├── actions │?? │?? └── actions.js │?? ├── components │?? │?? ├── index.js │?? │?? ├── Profile │?? │?? │?? ├── Profile.js │?? │?? │?? └── Profile.less │?? │?? └── Search │?? │?? ├── Search.js │?? │?? └── Search.less │?? ├── containers │?? │?? ├── App.js │?? │?? ├── App.less │?? │?? └── test.less │?? ├── index.html │?? ├── index.js │?? └── reducers │?? └── reducers.js └── webpack.config.js
其中containers放置我們的容器組件,components放置展示性組件,打包入口是index.js。
Demo Reduxstate
使用Redux非常重要的一點(diǎn)就是設(shè)計(jì)好頂層的state,在demo中我們需要的state大概長(zhǎng)這個(gè)樣子:
{ isFetchingData, // boolean username, // string profile, // object }
其中isFetchingData是網(wǎng)絡(luò)請(qǐng)求的狀態(tài),正在拉取數(shù)據(jù)為true,username是我們要獲取用戶信息的名字,profile是我們拉取用戶的詳細(xì)信息,這個(gè)將會(huì)是一個(gè)Ajax請(qǐng)求,最后由github API提供。
actions
同步action我們不再講述,上一篇文章已經(jīng)說(shuō)得比較清楚,這里我們重點(diǎn)說(shuō)異步action,app的所有action如下:
export const GET_INFO = "GET_INFO"; // 獲取用戶信息 export const FETCHING_DATA = "FETCHING_DATA"; // 拉取狀態(tài) export const RECEIVE_USER_DATA = "RECEIVE_USER_DATA"; //接收到拉取的狀態(tài) // async action creator export function fetchUserInfo(username) { return function (dispatch) { dispatch(fetchingData(true)); return fetch(`https://api.github.com/users/${username}`) .then(response => { console.log(response); return response.json(); }) .then(json => { console.log(json); return json; }) .then((json) => { dispatch(receiveUserData(json)) }) .then(() => dispatch(fetchingData(false))); }; }
上面網(wǎng)絡(luò)請(qǐng)求用到了fetch這個(gè)API,它會(huì)返回一個(gè)Promise,還比較新可以使用社區(qū)提供的polyfill或者使用純粹的XHR都行,這都不是重點(diǎn)。我們看看這個(gè)action生成函數(shù)返回了一個(gè)函數(shù),并且在這個(gè)函數(shù)中還有dispatch操作,我們通過中間件傳入的dispatch可以用來(lái)dispatch actions。在上面的promise鏈?zhǔn)街惺紫任覀兇蛴×薵ithub API返回Response object,然后輸出了json格式的數(shù)據(jù),然后dispatch了RECEIVE_USER_DATA這個(gè)action表示接收到了網(wǎng)絡(luò)請(qǐng)求,并需要修改state(注:這里我們沒有考慮網(wǎng)絡(luò)請(qǐng)求失敗的情況),最后我們dispatch了FETCHING_DATA并告訴對(duì)應(yīng)reducer下一個(gè)state的isFetchingData為false,表示數(shù)據(jù)拉取完畢。
reducer
這里看看最核心的reducer,操作profile這一塊的:
function profile(state = {}, action) { switch (action.type) { case GET_INFO: return Object.assign({}, state, { username: action.username, }); case RECEIVE_USER_DATA: return Object.assign({}, state, action.profile); default: return state; } } function isFetchingData() {...} function username() {...} const rootReducer = combineReducers({ isFetchingData, username, profile, }); export default rootReducer;
將拉取到的profile對(duì)象assign到之前的state,最后通過combineReducers函數(shù)合并為一個(gè)reducer。
連接到React我們通過react-redux提供的connect方法與Provider來(lái)連接到React,Provider主要的作用是包裝我們的容器組件,connect用于將redux與react進(jìn)行連接,connect() 允許你從 Redux store 中指定準(zhǔn)確的 state 到你想要獲取的組件中。這讓你能獲取到任何級(jí)別顆粒度的數(shù)據(jù),了解更多可以參考它的API,這里我們不再敖述。它的形式可以是這樣:
function mapStateToProps(state) { return { profile: state.profile, isFetchingData: state.isFetchingData, }; } function mapDispatchToProps(dispatch) { return { fetchUserInfo: (username) => dispatch(fetchUserInfo(username)) }; } class App extends Component { render() { const { fetchUserInfo, profile, isFetchingData } = this.props; return (); } } export default connect( mapStateToProps, mapDispatchToProps )(App);{"name" in profile ? : ""}
connect是個(gè)可以執(zhí)行兩次的柯里化函數(shù),第一次傳入的參數(shù)相當(dāng)于一系列的定制化東西,第二次傳入的是你要連接的React組件,然后返回一個(gè)新的React組件。第一次執(zhí)行時(shí)傳入的參數(shù)是mapStateToProps, mapDispatchToProps, mergeProps, options。也就是說(shuō)這里相當(dāng)于幫組容器選擇它在整個(gè)Store中所需的state與dispatch回調(diào),這些將會(huì)被connect以Props的形式綁定到App容器,我們可以通過React開發(fā)者工具看到這一點(diǎn):
第一次執(zhí)行,選擇需要的state,第二次傳入App容器組件然后返回新的組件。然后創(chuàng)建整個(gè)應(yīng)用的store:
const loggerMiddleware = createLogger(); const store = createStore( rootReducer, compose( applyMiddleware( thunkMiddleware, loggerMiddleware, ), window.devToolsExtension ? window.devToolsExtension() : f => f ) );
這里我們用到了兩個(gè)中間件,loggerMiddleware用于輸出我們每一次的action,可以明確的看到每次action后state的前后狀態(tài),thunkMiddleware用于網(wǎng)絡(luò)請(qǐng)求處理,最后window.devToolsExtension ? window.devToolsExtension() : f => f是為了連接我們的redux-dev-tool,可以明確的看到我們dispatch的action,還能達(dá)到時(shí)間旅行的效果。最后通過Provider輸入我們的store,整個(gè)應(yīng)用就跑起來(lái)啦!
let mountRoot = document.getElementById("app"); ReactDOM.render(Run, mountRoot );
命令行輸入npm run dev,整個(gè)應(yīng)用就跑起來(lái)了,在輸入框輸入Jiavan,我們來(lái)看看action與數(shù)據(jù)流:
在console面板,logger中間件為我們打印除了每一次dispatch action以及前后的state值,是不是非常直觀,然而厲害的還在后面。redux-dev-tool可以直接查看我們state tree以及對(duì)action做undo操作達(dá)到時(shí)間旅行的效果!
完整的demo在文章最后將會(huì)貼出,現(xiàn)在總結(jié)下:首先我們規(guī)劃了整個(gè)應(yīng)用的state,然后進(jìn)行數(shù)據(jù)流層的代碼開發(fā),同步異步action的編寫以及reducer的開發(fā),再通過選擇我們?nèi)萜鹘M件所需的state與dispatch回調(diào)通過connect方法綁定后輸出新的組件,通過創(chuàng)建store與Provider將其與React連接,這樣整個(gè)應(yīng)用的任督二脈就被打通了。最后極力推薦Redux的官方文檔。
完整demo -> https://github.com/Jiavan/rea...
運(yùn)行
1. npm install 2. webpack 3. npm run dev參考文章
http://cn.redux.js.org/docs/r...
http://www.jianshu.com/p/ab9e...
原文出處 https://github.com/Jiavan/jia... 覺得對(duì)你有幫助就給個(gè)star吧
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/80220.html
摘要:異步實(shí)現(xiàn)設(shè)計(jì)需要增加三種通知異步請(qǐng)求發(fā)起的異步請(qǐng)求成功的異步請(qǐng)求失敗的示例代碼如下返回參數(shù)完全可以自定義。這種分別在請(qǐng)求開始前,請(qǐng)求成功后,請(qǐng)求失敗后發(fā)送。表示數(shù)據(jù)的有效性,他的作用是在異步請(qǐng)求發(fā)送失敗后,告訴當(dāng)前的數(shù)據(jù)是過時(shí)的數(shù)據(jù)。 說(shuō)明:對(duì)Redux不了解的同學(xué)可先看看這篇文章Redux技術(shù)架構(gòu)簡(jiǎn)介(一) 前言 這里說(shuō)的Redux異步實(shí)現(xiàn),是專指Redux中的異步Action實(shí)現(xiàn),...
摘要:舉例來(lái)說(shuō)一個(gè)異步的請(qǐng)求場(chǎng)景,可以如下實(shí)現(xiàn)任何異步的邏輯都可以,如等等也可以使用的和。實(shí)際上在中,一個(gè)就是一個(gè)函數(shù)。 書籍完整目錄 3.4 redux 異步 showImg(https://segmentfault.com/img/bVyou8); 在大多數(shù)的前端業(yè)務(wù)場(chǎng)景中,需要和后端產(chǎn)生異步交互,在本節(jié)中,將詳細(xì)講解 redux 中的異步方案以及一些異步第三方組件,內(nèi)容有: redu...
摘要:基本流程創(chuàng)建帶有三個(gè)方法發(fā)出處理數(shù)據(jù)每次后的數(shù)據(jù)即得到的數(shù)據(jù)即一個(gè)新的所以是一個(gè)對(duì)象每次都是上次返回的值所以用即返回的新狀態(tài)新的即本次的返回值所以每次都是往空的對(duì)象里先推再新增屬性或改變?cè)瓉?lái)屬性的值層通過方法設(shè)置監(jiān)聽函數(shù)一旦發(fā)生變化就會(huì) 基本流程 1.創(chuàng)建store,帶有三個(gè)方法:store.dispatch,store.subscribe,store.getState import ...
摘要:在函數(shù)式編程中,異步操作修改全局變量等與函數(shù)外部環(huán)境發(fā)生的交互叫做副作用通常認(rèn)為這些操作是邪惡骯臟的,并且也是導(dǎo)致的源頭。 注:這篇是17年1月的文章,搬運(yùn)自本人 blog... https://github.com/BuptStEve/... 零、前言 在上一篇中介紹了 Redux 的各項(xiàng)基礎(chǔ) api。接著一步一步地介紹如何與 React 進(jìn)行結(jié)合,并從引入過程中遇到的各個(gè)痛點(diǎn)引出 ...
摘要:是官方文檔中用到的異步組件,實(shí)質(zhì)就是一個(gè)中間件,簡(jiǎn)單來(lái)說(shuō)就是一個(gè)封裝表達(dá)式的函數(shù),封裝的目的是延遲執(zhí)行表達(dá)式。這時(shí)我們需要對(duì)一般異步中間件進(jìn)行處理。 曾經(jīng)前端的革新是以Ajax的出現(xiàn)為分水嶺,現(xiàn)代應(yīng)用中絕大部分頁(yè)面渲染會(huì)以異步流的方式進(jìn)行。在Redux中,如果要發(fā)起異步請(qǐng)求,最合適的位置是在action creator中實(shí)現(xiàn)。但我們之前了解到的action都是同步情況,因此需要引入中間...
閱讀 3339·2021-11-24 09:39
閱讀 3913·2021-11-22 09:34
閱讀 4877·2021-08-11 11:17
閱讀 1093·2019-08-29 13:58
閱讀 2622·2019-08-28 18:18
閱讀 570·2019-08-26 12:24
閱讀 863·2019-08-26 12:14
閱讀 768·2019-08-26 11:58