之前利用知乎日報的api寫了react-native的一個入門專案,傳送文章地址React Native 專案入門和原始碼地址RN入門專案原始碼,目前github上的程式碼已經在原文的基礎上增加了新的功能,也就是本文進階的內容,看完本文感興趣的同學可以參考一下。
思考
在入門專案中我們已經實踐了React native 的基本用法,展示了知乎日報的列表和詳情頁。在小型的專案中這樣實踐當然是沒有問題的,我們在每個頁面中自行請求網路,渲染元件,完全可以。但是當專案變得複雜,需要增加新的feather或者擴充套件log日誌等基礎模組時,這樣的實踐絕對讓人苦不堪言,事倍功半。
從維護和功能擴充套件等角度來說,專案使用合適的框架是必需的。我們從日誌入手來分析,比如我們需要記錄系統中所有訪問網路的日誌,如果每個頁面中使用fetch訪問網路,則log需要記錄在每個頁面中進行記錄:
一種進階的方法是封裝網路請求方法,日誌記錄統一封裝模組中進行設定:
這種方法已經可以應付大部分情況,當log需求更改時,只需跟新一個模組即可。
如果這時需要增加資料庫訪問的日誌,我們按照相同的思路,可以定義一個資料庫訪問的模組,在該模組中定義相應的日誌模組。如果需要增加更多型別的日誌模組呢,是繼續按照同樣的思路繼續嗎?
我們看一下剛提到的兩種日誌,提取一下共同點,都需要記錄日誌,我們有沒有可能提取一個日誌模組,圖示一下思路:
假如這時又有一個需求是記錄一下所有操作的執行時間,並根據執行時間的統計對相應的系統引數進行調整,這時候怎麼解決呢,圖示思路,我們可以在中間繼續增加一個模組:
以上均是一些簡單的思路,在具體工程實踐中應該怎麼做呢?我們先來看一下Flux的思路。
Flux
我們先來看一下Flux中的幾個角色:
- View: 檢視層
- Action(動作):檢視層發出的訊息(比如mouseClick)
- Dispatcher(派發器):用來接收Actions、執行回撥函式
- Store(資料層):用來存放應用的狀態,一旦發生變動,就提醒Views要更新頁面
其基本思路是對所有的動作進行封裝,然後使用dispatcher對動作進行分發,在Store中完成處理後傳遞給View,完成一次完整資料流動,此流動為單向,不可逆。
其中Action我們理解為動作,此動作不僅包括動作型別(即目標,記錄日誌還是計算任務執行時間),同時還可以攜帶一定的資料,參照以上我們的思路圖4,非常符合我們的需求。
Redux
Redux 實際是flux思路的一種實現,在角色定義方面略有不同,但是不妨礙思路相同。
- Action(動作) 與flux中的定義類似
- reducer 對Action中定義是state進行更新,注意流程中只能通過reduce進行更新,同時reducer要保證為純函式,即對於一定對輸入,一定可以獲得相應的輸出,而不會受到時間或者因此的影響。
- store store 實際完成了dispatcher的功能,同時連線了其他功能模組。
維持應用的 state;
提供 getState() 方法獲取 state;
提供 dispatch(action) 方法更新 state;
通過 subscribe(listener) 註冊監聽器;
通過 subscribe(listener) 返回的函式登出監聽器。
redux還有一個重要的概念:中介軟體,中介軟體可以在資料傳遞過程中進行一些額外的動作,如上文提到的日誌記錄等功能。至此,我們可以利用redux完成我們需要的功能。本文主要介紹思路,關於redux的具體學習推薦阮一峰老師的部落格,Redux 入門教程。
redux-saga
我們知道在應用中一定會有很多非同步操作,如網路訪問、資料讀取等,redux完成了資料的傳遞,但是在非同步操作部分如果我們不加註意,可能會寫出一團糟的程式碼,redux-saga正完成了這件事情。
Sagas 負責協調那些複雜或非同步的操作。
專案實踐
本文的專案進階就是在程式碼中加入對redux的支援,當然按照本專案的需求增加這些是沒有必要的,只是徒增了專案的複雜度。這裡主要演示redux以及中介軟體redux-saga, redux-logger在專案中的使用。其中saga在以上已經進行了介紹,redux logger 實際是對所有Action state 進行了紀錄,進行專案debug時,我們可以根據console的log輸出判斷專案的具體state變化:
- store 的建立
const middlewares = [];
// 建立reducer
const rootReducer = combineReducers({story});
// 建立中介軟體saga
const sagaMiddleware = saga();
middlewares.push(sagaMiddleware)
if (process.env.NODE_ENV === 'development') {
//建立中介軟體logger
const logger = createLogger();
middlewares.push(logger);
}
//applymiddleware配置中介軟體
const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore);
function createDefaultStore(initialsState) {
//通過reducer 獲取stare
const defaultStore = createStoreWithMiddleware(rootReducer, initialsState);
return defaultStore;
}
const store = createDefaultStore();複製程式碼
Reducers的建立
可以建立多個reducers,通過combineReducers();複製程式碼
進行組合
const initialState = { id: "", refreshing: true, loaded: false, story: new Object() }; export default function story(state = initialState, action) { switch (action.type) { case ActionType.Fetch_Story_Detail: return Object.assign({}, state, { id: action.id, refreshing: true, loaded: false }); case ActionType.Fetch_Story_Detail_Done: return Object.assign({}, state, { id: action.id, refreshing: false, load: true, story: action.story }); default: return state; } }複製程式碼
如上建立store reducer
connect
介面元件需要connect redux,可以dispatch Actions,同時可以接收到相應的回撥,完成介面的render。function mapStateToProps(state) { const {story} = state; return { story }; } StoryDetailPage.propTypes = propTypes; export default connect(mapStateToProps)(StoryDetailPage);複製程式碼
經過這幾步,我們專案的結構就非常清晰了,在進行模組維護和擴充套件時就很方便了。
程式碼地址:react-native-zhihu
歡迎關注公眾號wutongke,每天推送移動開發前沿技術文章:
推薦閱讀:
React-native專案入門與思考
React native 專案入門(知乎日報,豆瓣電影,[one]一個)
React native 專案進階(redux, redux saga, redux logger)
React Native 專案2(One 【一個】客戶端)
感謝:
www.ruanyifeng.com/blog/2016/0…
www.ruanyifeng.com/blog/2016/0…
github.com/kenberkeley…
leonshi.com/redux-saga-…
cn.redux.js.org/index.html