1. 程式人生 > >自己實現一個Redux。

自己實現一個Redux。

Redux核心

Redux是一個用於狀態管理的js框架,是Flux架構的一種實現(如圖)。
Flux

Redux核心概念

  • store
    是一個物件,所有狀態集中儲存的地方,並全域性唯一,稱為狀態樹。幷包含以下幾個操作方法:
    • getState()獲取當前的狀態樹;
    • dispatch(action)分發一個動作,以改變狀態樹中對應的狀態;
    • subscribe(listener)新增一個監聽器,以監聽狀態樹的變化,當狀態樹變化時,會馬上執行傳入的函式。會返回一個unsubscribe函式,執行該函式可以解綁該監聽器。
    • replaceReducer(nextReducer)用於熱載入,暫時不重要。
  • reducer
    為一個使用者自定義的函式,在store分發(dispacth)action時提供處理方法去更新狀態樹中的狀態。應該為一個純函式。
  • action
    為一個使用者自定義的物件,裡面包含一個type屬性,標識一個動作型別。

核心概念總結:store是儲存所有狀態的地方,改變狀態的唯一方法就是通過呼叫store的dispatch方法分發相應的動作,該動作會經過每個使用者定義的reducer,reducer通過識別action的type,以及接收原來的狀態返回一個新的狀態。

redux核心api

  • createStore(reducer, initState)根據使用者定義的reducer建立一個store。第二個引數為初始狀態,可選;
  • combineReducers(reducers)把使用者定義的多個reducer合併為一個。

逐步程式碼實現(涉及ES6語法):

createStore

1、createStore函式的大概樣子:

const createStore = (reducer, initState)=>{
	const state = initState || {};	//初始狀態;
	
	return {	//store的核心操作方法;
		getState(){
			return state;
		}
		
		dispatch(action){}
		
		subscribe(){}
	};
}

2、dispatch(action)方法實現:
dispatch方法是比較複雜的一個方法,應做的工作有:檢測action,檢測及防止dispatch死迴圈,觸發監聽器。

const createStore = (reducer, initState)=>{
	let state = initState || {};		//初始狀態;
	/*  新增程式碼  */
	let inDispatch = false;		//是否正在分發action
	const listeners = [];		//監聽器
	/* *********** */
	
	return {	//store的核心操作方法;
		getState(){
			return state;
		}
		
		dispatch(action){
		/*  新增程式碼  */
			//1、檢測action合法性
			if(!action || typeof action !== 'object' || !action.type){
				throw new Error('action invalid');
			}
			//2、檢測是否正在分發,以防止在reducer中呼叫dispatch而導致死迴圈。
			if(inDispatch){
				throw new Error('can not dispatch a action now!');
			}
			//3、分發action
			try{
				inDispatch = true;
				state = reducer(state, action);
			}finally{
				inDispatch = false;
			}
			//4、觸發所有監聽器
			listeners.forEach(listener => listener());
		/* *********** */
		}
		
		subscribe(){}
	};
}

3、subscribe(listener)方法實現:
subscribe方法除了新增監聽器之外,還應檢測重複監聽,返回一個解綁函式。

const createStore = (reducer, initState)=>{
	let state = initState || {};		//初始狀態;
	let inDispatch = false;		//是否正在分發action
	const listeners = [];		//監聽器
	
	return {	//store的核心操作方法;
		getState(){
			return state;
		}
		
		dispatch(action){
			//1、檢測action合法性
			if(!action || typeof action !== 'object' || !action.type){
				throw new Error('action invalid');
			}
			//2、檢測是否正在分發,以防止在reducer中呼叫dispatch而導致死迴圈。
			if(inDispatch){
				throw new Error('can not dispatch a action now!');
			}
			//3、分發action
			try{
				inDispatch = true;
				state = reducer(state, action);
			}finally{
				inDispatch = false;
			}
			//4、觸發所有監聽器
			listeners.forEach(listener => listener());
		}
		
		subscribe(listener){
		/*  新增程式碼  */
			if(listeners.some(item => item===listener))return;	//檢測重複監聽
			
			const pos = listeners.push(listener) - 1;			//記錄該監聽器的位置
			return ()=>{
				//返回函式為刪除該位置的監聽器
				if(listeners[pos] === listener){
					listeners.splice(pos,1);
				}
			};		
		/* *********** */
		}
	};
}

這樣,Redux的核心功能就實現了。

combineReducers

實現combineReducers 應該考慮到多層、多個reducer的情況,並且能自動將action逐層分發至每個子reducer。
1、combineReducers大概的樣子:

const combineReducers = (reducers){
	if(!reducers || typeof reducers !== 'object'){
		throw new Error('invalid reducers.');
	}
	return (action)=>{};
}

2、關鍵程式碼實現:

const combineReducers = (reducers){
	if(!reducers || typeof reducers !== 'object'){
		throw new Error('invalid reducers.');
	}
	return (state, action)=>{
	/*  新增程式碼  */
		const newState = {};
		for(key in reducers){
			newState[key] = reducers[key](state[key], action);
		}
		return newState;
	/* *********** */
	};
}