管理整個前端項目(單頁應用)全部的狀態數據,統一把整個應用的狀態存到一個地方(store),保存成一個狀態樹,修改數據須要派發(dispatch)一個動做(action)通知store修改。組件經過訂閱(subscribe)修改事件,獲取最新數據來修改自身狀態。javascript
從源碼出發,整個redux源碼,總共導出5個函數,和一個__DO_NOT_USE__ActionTypes
(默認的action,全部action類型不能重複,下面會細撩這個點),那麼下面就來細細的撩一下5個函數。前端
想要使用redux,首先就是建立一個全局的store(固然是惟一的一個),就得調用這個函數(createStore)。該函數接收三個參數。store裏面保存了全部的數據,能夠看作一個容器。java
傳入reducer
和initState
建立store。react
store返回給鑰匙
,修改器
,電話
。git
有了鑰匙就能隨時取數據,若是須要修改數據就得經過修改器,如須要須要知道數據何時修改了,就打一個電話給store,告訴它,數據修改好了給我說。github
返回當前的store狀態樹,包含全部state。這裏在讀源碼的時候發現一個疑問。 redux
帶着這個疑問,給Redux提了一個PR. 獲得回覆:app
Yes - getState() just returns whatever the current state value is. It's up to you to avoid accidentally mutating it.ide
(譯文)是的,getState()只返回當前狀態值。你要避免不當心把它弄亂。函數
也就是說在這裏須要注意一下,getState()返回的值不能直接修改,不然你將陷入深淵
傳入一個函數用來監聽store發生變化,一旦store發生了變化,將循環執行全部的監聽函數。調用該函數還返回了一個取消監聽的函數。調用返回的函數,則取消該listener監聽。
dispatch接收一個action。在函數內部會把全部的reducer執行一遍並把當前的state和action傳入reducer,而後再執行全部的監聽函數。從源碼中截了一段出來:
const dispatch = (action) => {
currentState = currentReducer(currentState, action);
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
複製代碼
一個對象,必須包含一個type,表示所要發生的動做,其餘屬性自由設置,社區中有一個規範,把其餘全部數據放入payload中,通常開發中會寫一個函數來生成action。
function login(){
retrun {
type: 'LOGIN',
payload: {
username: 'xxx',
passworld: 'xxxx'
}
}
}
複製代碼
傳入新的reducer,替換以前的reducer,而且派發一個ActionType爲REPLACE的action。
一個純函數,接收當前的state和action作爲參數,返回新的state。
Store 收到 Action 之後,會調用reducer,必須給出一個新的 State,這樣 Store裏的數據纔會發生變化。
編寫一個簡單的reducer
const reducer = function (state, action) {
switch(action.type){
case 'TYPE':
return {...state, newState}
default:
return state;
}
};
複製代碼
源碼裏面的參數名叫這個,其實我更願意叫它initState,初始化的狀態,爲何源碼不叫initState呢?由於不許確,在源碼裏會默認發送一個type爲init的dispatch(),這個時候走到reducer裏面(看看上面的reducer代碼),state若是在這個時候設置一個默認值,好比:
const reducer = function (state = {list: []}, action) {
switch(action.type){
case 'TYPE':
return {...state, newState}
default:
return state;
}
};
複製代碼
這個時候就默認返回了{list: []},就給出了一個真正的initState。
用來配合快速建立中間件的。
上面提到的__DO_NOT_USE__ActionTypes
,就是2個actionType:
@@redux/INIT
: 用來內部發送一個默認的dispatch@@redux/REPLACE
: 用來替換reducer遍歷全部生成action函數並執行,返回被dispatch包裹的函數,可供直接調用派發一個請求。
該參數爲一個對象,包含生成action的函數,例如:
const actionCreators = {
login: () => {
return {
type: 'LOGIN',
payload: {
username: 'xxx',
passworld: 'xxxx'
}
}
},
logout: () => {
retrun {
type: 'LOGOUT'
}
}
}
複製代碼
若是傳入一個函數,則執行函數獲得action,返回一個dispatch(action)。
這裏就是createStore所返回的dispatch
在項目開發中,須要分模塊寫reducer,利用該函數合併多個reducer模塊。傳入一個reducer集合。
a.js
export (state = {list: []}, action) => {
switch (action.type) {
case "LIST_CHANGE":
return { ...state, ...action.payload };
default:
return state;
}
}
b.js
export (state = {list: []}, action) => {
switch (action.type) {
case "LIST":
return { ...state, ...action.payload };
default:
return state;
}
}
combineReducers.js
import a from './a';
import b from './b';
combineReducers({
a: a,
b: b
})
複製代碼
a
和b
都有list這個state,可是他們並不相關,要把他們分開使用,就得用combineReducers去合併。
下面簡單實現了該函數:
function combineReducers(reducers) {
return (state = {}, action) => {
return Object.keys(reducers).reduce((currentState, key) => {
currentState[key] = reducers[key](state[key], action);
return currentState;
}, {})
};
}
複製代碼
能夠說是js中函數式中很重要的方法,把一堆函數串聯起來執行,從右至左執行(也就是倒序),函數的參數是上一個函數的結果。看一個使用例子:
const fn1 = (val) => val + 'fn1';
const fn2 = (val) => val + 'fn2';
const fn3 = (val) => val + 'fn3';
const dispatch = compose(fn1, fn2, fn3);
console.log(dispatch('test'));
複製代碼
最終輸出結果testfn3fn2fn1
test傳給fn3當參數,fn3的返回值給了fn2....
function compose(...fns){
if(fns.length==1) return fns[0];
return fns.reduce((a,b)=>(...args)=>a(b(...args)));
}
複製代碼
該函數是用來添加中間件,在修改數據的時候作一些其餘操做,redux經過改造dispatch來實現中間件.
使用中間件
const middleware = applyMiddleware(logger);
複製代碼
須要傳入中間件函數,能夠是多個函數,依次傳入,在applyMiddleware裏面用到了compose,因此咱們傳入的函數的從右至左依次執行的,這裏須要注意一下。
const createStoreWithMiddleware = middleware(createStore);
複製代碼
由於中間件是經過改造dispatch來實現,因此須要把建立store的方法傳進去。
const store = createStoreWithMiddleware(reducer, preloadedState)
複製代碼
這裏再傳入createStore須要接收的參數,返回store對象。
const logger = function({dispatch, getState}){
return function(next){
return function(action){
console.log('oldState',getState());
next(action); // 真實派發動做
console.log('newState',getState());
}
}
}
複製代碼
首先middleware會把未改造的dispatch
和getState
傳入進來,這裏的next
至關於dispatch,去派發一個真正的修改數據動做。
源碼貼上:
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
複製代碼
middlewareAPI
是存儲未改造的方法,compose
上面講過,第一個參數傳入的就是dispatch,返回的一個改造後dispatch就是經過compose事後的一個函數,會依次執行。
一些學習心得,若有不對歡迎指正
個人github 謝謝你閱讀個人文章