redux的實現過程
Redux實現過程
不同組件需要依賴同一個數據的時候,就需要狀態提升至這些組件的根組件。
redux是狀態統一管理工具,需要使用它的原因是: 組件之間通信統一管理,方便代碼維護。
React中有一個特性context,只要某個組件使用context存儲了數據,那麽這個組件的所有子組件都可以訪問該context內容,並且還可以修改它。就像是這個組件的全局變量,它的所有子組件都可以訪問這個全局變量。
如下圖,假設要更改主題顏色,那麽在Index根組件的context中存儲當前主題色,那麽它的子組件header,footer,title,menu都可以訪問到,並且可以修改。
Redux的統一數據管理,與React的關聯之處利用的就是context特性
但是context有一個缺點就是,所有的子組件都可以修改共享的內容,每個組件都能夠改 context 裏面的內容會導致程序的運行不可預料。所以React團隊的做法就是,提高修改的門檻,修改數據時統一調用dispatch函數,並且需要傳入修改類型,如果dispatch中不存在該類型,則不允許修改。
接下來簡單推理一下redux的實現過程(假設有組件Index,Header,Footer)。
1、第一步是需要有一個存儲數據的對象,定義為appState
var appState = {
themeColor:‘red‘,
themeBackground:‘black‘
};
//還需要一個修改數據的函數
function dispatch(action){
switch(action.type){
case ‘UPDATE_THEME_COLOR‘:
appState.themeColor = action.color;break;
case ‘UPDATE_THEME_BACKGROUND‘ :
appState.themeBackground = action.color;break;
default break;
}
}
//修改完數據,就需要重新渲染了,定義渲染函數
function renderApp(state){
renderHeader(state);
renderFooter(state);
}
function renderHeader(state){
header = document.getElementById (‘header‘);
header.style.color = state.color;
header.style.backgroundColor = state.themeBackground;
}
function renderFooter(state){
title = document.getElementById(‘footer‘);
footer.style.color = state.color;
footer.style.backgroundColor = state.themeBackground;
}
dispatch({type:‘UPDATE_THEME_COLOR‘,color:‘green‘})//修改主題色
renderApp(appState)//執行重新渲染
2、封裝state和dispatch,命名為store,方便復用,同時添加監聽,當數據變化時,通知訂閱者重新渲染
function createStore(state,stateChanger){
const getState = () => state;//獲取數據
const listeners = [];
const subscribe = (listener) => listeners.push(listener);
const dispatch = (action) => {
stateChanger(state, action);
listeners.forEach((listener)=>listener());
};
return {getState,dispatch,subscribe};
}
//實例化一個store。為了統一命名,dispatch改為stateChanger,appState改名為state
const store = createStore(state,stateChanger);
renderApp(store.getState());//首次渲染
store.subscribe(renderApp(store.getState()));//添加訂閱者,監聽到變化就重新渲染數據
store.dispatch({type:‘UPDATE_THEME_COLOR‘,color:‘green‘});//修改主題
3、第二步有一個很大的缺陷就是,每次修改數據就重新全部渲染一遍,對性能影響很大。
優化點是: 判斷數據是否有變化,如果沒變化就不需要重新渲染,另外stateChanger與state合為一體。
//此處用到es6的淺復制,例如
let a = { name:‘HAPPY‘,attr:{age:23},common:{sex:‘女‘}};
let b = {...a,attr:{age:24}};//b為{name:‘HAPPY‘,attr:{age:24},common:{sex:‘女‘}}
a.name===b.name//true
a.attr===b.attr//false
a.common===b.common//true
//這樣有50%以上的復用率
function stateChanger(state,action){
if(!state){
return {
themeColor:‘red‘,
themeBackground:‘black‘
}
}
const newState = {...state};
switch(action.type){
case ‘UPDATE_THEME_COLOR‘:
return {
...newState,
themeColor: action.color
};
case ‘UPDATE_THEME_BACKGROUND‘:
return {
...newState,
themeBackground: action.color
};
default
return{
...newState
};
}
}
//那麽渲染的時候需要知道oldState和newState,這樣才能對比數據的變化
function renderApp(state,oldState={}){//此處oldState放在後面,並且給默認值,是為了兼容首次渲染,首次渲染olsState是沒有值的,所以給默認值
if(oldState===state){
return;
}
renderHeader(oldState,state);
renderFooter(oldState,state);
}
//如果renderHeder需要的渲染的數據是state內的子對象,那麽在renderHeader渲染之前,也需要判斷一下數據是否有變化,此處舉的例子state結構簡單,所以不需要判斷。
function createStore(stateChanger){//此處是優化state與stateChanger結合。
let state = null
const getState = () => state;//獲取數據
const listeners = [];
const subscribe = (listener) => listeners.push(listener);
const dispatch = (action) => {
state = stateChanger(state, action);
listeners.forEach((listener)=>listener());
};
return {getState,dispatch,subscribe};
}
//最後需要oldState,那麽我們就需要定義oldState
const store = createStore(stateStranger);
const oldState = store.getState();
store.subscribe(()=>{
const newState = store.getState();
renderApp(oldState,newState);
oldState = newState;
});//添加監聽
renderApp(store.getState())//首次渲染
store.dispatch({type:‘UPDATE_THEME_COLOR‘,color:‘green‘});//修改主題
4.此時的stateChanger是一個純函數,就是內部邏輯只與參數有關,並且無副作用(也就是對其他數據沒有任何影響)。它要做的僅僅是 —— 初始化和計算新的 state。(並不會修改state,因為我們每次返回的都是新的對象)
而這個函數就是redux中的reducer,那麽我們給stateChanger改名字為reducer.現在我們的redux就實現完成了。
function reducer(action){...}
function createStore(reducer){...}
//接下來我們就可以定義不同的reducer,生成不同的store了,並且修改監聽,例如
function themeReducer(action){...}
const store = createStore(themeReducer);
// 監聽數據變化重新渲染頁面
store.subscribe(() => renderApp(store.getState()))
// 首次渲染頁面
renderApp(store.getState())
// 後面可以隨意 dispatch 了,頁面自動更新
store.dispatch(...)
但是我們怎麽跟react進行連接呢?怎麽把redux用在react中呢?就需要react-redux來連接。下一篇繼續
參考教程:(http://huziketang.mangojuice.top/books/react)
redux的實現過程