Redux源碼分析之createStore
接著前面的,我們繼續,打開createStore.js, 直接看最後, createStore返回的就是一個帶著5個方法的對象。
同樣的,我先刪除一些不需要的代碼,簡化成如下, 注意看備註。(註:這裡先無視中間件和enhancer,後篇再說)
export const ActionTypes = {
INIT: "@@redux/INIT"
}
export default function createStore(reducer, preloadedState, enhancer) {
// 初始化參數
let currentReducer = reducer // 整個reducer
let currentState = preloadedState //當前的state, getState返回的值就是他,
let currentListeners = // 當前的訂閱,搭配 nextListeners
let nextListeners = currentListeners //下一次的訂閱 ,搭配currentListeners
let isDispatching = false //是否處於 dispatch action 狀態中
// 內部方法
function ensureCanMutateNextListeners { } // 確保currentListeners 和 nextListeners 是不同的引用
function getState { } // 獲得當前的狀態,返回的就是currentState
function subscribe(listener) { } //訂閱監聽,返回一個函數,執行該函數,取消監聽
function dispatch(action) { } // dispacth action
function replaceReducer(nextReducer) { } // 替換 reducer
function observable { } //不知道哈哈
//初始化state
dispatch({ type: ActionTypes.INIT })
//返回方法
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
ensureCanMutateNextListeners
這個方法主要用在 subscribe裡面,
- 在每次訂閱和取消訂閱的時候,會讓 nextListeners 和 currentListeners 不是同一個引用,
- 在每次 dispatch的時候,當 reducer執行完畢,訂閱執行前,讓 nextListeners 和 currentListeners 是一個引用
function ensureCanMutateNextListeners {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice
}
}
為什麼這麼設計,在subscribe方法上有很詳細的註解,我的理解是假如訂閱在執行過程中,這裡說的是訂閱執行過程,不是reducer執行過程
有新加的訂閱添加的時候,新的訂閱是不會被執行的,因為是一份拷貝
有新的訂閱刪除的時候,被刪除的還是會執行的。
簡單說,就是新的刪除和添加,下次生效。
getState
就是返回利用閉包存的currentState
/**
* Reads the state tree managed by the store.
*
* @returns {any} The current state tree of your application.
*/
function getState {
return currentState
}
subscribe
添加訂閱
- 每次添加前,如果 nextListeners 和 currentListeners 是一個引用,重新複製一個
- 並存入 nextListeners
- 返回一個函數,執行該函數取消訂閱,
function subscribe(listener) {
if (typeof listener !== "function") {
throw new Error("Expected listener to be a function.")
}
let isSubscribed = true
ensureCanMutateNextListeners //複製新的
nextListeners.push(listener)
return function unsubscribe {
if (!isSubscribed) {
return
}
isSubscribed = false
ensureCanMutateNextListeners // 複製新的
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1) // 從nextListeners裡面刪除,下次dispatch會生效
}
}
dispatch
派發一個action,讓reducer更新數據,下面都有注釋了,為啥可說的。
- 如果上一次派發沒完畢,接著派發是會出異常的,對於單線程的js來說倒是沒啥大問題
function dispatch(action) {
if (!isPlainObject(action)) { // action 必須是對象
throw new Error(
"Actions must be plain objects. " +
"Use custom middleware for async actions."
)
}
if (typeof action.type === "undefined") { // 必須有type屬性
throw new Error(
"Actions may not have an undefined "type" property. " +
"Have you misspelled a constant?"
)
}
if (isDispatching) { // 正在派發,拋出異常
throw new Error("Reducers may not dispatch actions.")
}
try {
isDispatching = true // 標記,正在派發
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false //標記派發完畢
}
const listeners = currentListeners = nextListeners // 讓nextListeners生效
for (let i = 0; i < listeners.length; i++) { // 挨個執行訂閱
const listener = listeners[i]
listener
}
return action // 返回action
}
replaceReducer
function replaceReducer(nextReducer) {
if (typeof nextReducer !== "function") { // 不是函數,拋出異常
throw new Error("Expected the nextReducer to be a function.")
}
currentReducer = nextReducer // 替換reducer
dispatch({ type: ActionTypes.INIT }) // 重新初始化
}
observable 還沒啥研究,暫不說了。
最後的代碼為,
- 初始化 state
- 返回相關方法
dispatch({ type: ActionTypes.INIT })
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
這裡說一下 dispatch({ type: ActionTypes.INIT }) 是怎麼達到初始化state的,
我們再回頭看一下disptach中的一段代碼
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
這裡先講非合成的reducer,帶合成的後面說。
createStore的第一個參數為 reducer,第二個為初始化state的默認值,
- 如果你傳入了第二個參數,currentState就等於你傳入的值,而執行一次 dispatch的時候,系統定義的 type 為@@redux/INIT的action,你肯定是沒有定義的額,看下面代碼,就會直接返回state, 那麼執行 currentReducer(currentState, action) 得到的結果還是 currentState
- 如果你沒有傳入第二個參數,在reducer的第一個參數指定了默認值,那麼reducer處理type為 @@redux/INIT的action的時候,返回的就是reducer第一個參數 state的默認值,然後被賦值給了currentState
- 如果沒有傳入第二個參數,同時reducer的state也沒指定值,那麼,你的dispatch一般都會報錯,因為你的state從開始就是undefined
- 如果recuder函數和createStore都設置了默認了,那麼reducer的默認值是不會生效的
let todoReducer = function (state = todoList, action) {
switch (action.type) {
case "add":
return [...state, action.todo]
case "delete":
return state.filter(todo => todo.id !== action.id)
default:
return state
}
}
這裡相對特別的是 合成recuder,後面再說。
到此為止,你只用redux的createStore方法,就能完成數據控制了,combineReducers,bindActionCreators,applyMiddleware,compose 都只是對redux的增強。
再回頭看看我們第一篇提到的代碼:(雲淡風輕)
- 初始化的state在recuder哪裡賦值,和createStore賦值是等價的,都賦值的話,createStore的賦值會生效。
/* 簡單示例 */
let { createStore } = self.Redux
//默認state
let todoList =
// reducer
let todoReducer = function (state, action) {
switch (action.type) {
case "add":
return [...state, action.todo]
case "delete":
return state.filter(todo => todo.id !== action.id)
default:
return state
}
}
//創建store
let store = createStore(todoReducer,todoList)
//訂閱
function subscribe1Fn {
console.log(store.getState)
}
let sub = store.subscribe(subscribe1Fn)
store.dispatch({
type: "add",
todo: {
id: 1,
content: "學習redux"
}
})
store.dispatch({
type: "add",
todo: {
id: 2,
content: "吃飯睡覺"
}
})
store.dispatch({
type: "delete",
id: 2
})
// 取消訂閱
sub
console.log("取消訂閱後:")
store.dispatch({
type: "add",
todo: {
id: 3,
content: "打遊戲"
}
})
※線程和流的歷史遺留
※ES6 變數、常量聲明總結
※PHP輸出函數print, printf, sprintf的區別
TAG:科技優家 |
※React Native BackHandler exitApp 源碼分析
※網關 Spring-Cloud-Gateway 源碼解析——路由之RouteDefinitionLocator一覽
※HikariCP源碼分析之leakDetectionThreshold及實戰解決Spark/Scala連接池泄漏
※Execotors、Future、Callable和FutureTask的使用與源碼分析
※Flutter圖片緩存 Image.network源碼分析
※閱讀Android源碼BitmapFactory
※Selenium3源碼之common package篇
※kafka 源碼分析 3 : Producer
※ThreadLocal源碼分析
※druid-spring-boot-starter源碼解析
※Vue 源碼分析之 Observer
※Apache Storm流計算模型 及WordCount源碼實踐
※AtomicInteger 源碼解析
※jQuery源碼分析之jQuery.event.fix方法五問
※Spark 源碼分析之ShuffleMapTask內存數據Spill和合併
※JDK源碼閱讀:InterruptibleChannel 與可中斷 IO
※分散式共享 Session之SpringSession 源碼細節
※Kafka 源碼分析 5 :KafkaConsumer 消費處理
※Rasa Core源碼之Policy訓練
※JDK 源碼閱讀 Reference