繼上一篇文章《爲何 redux 要返回一個新的 state 引起的血案》以後,過了半個月我彭三漢又回來了,直接看源碼,github 戳這裏,咱們能夠看到這樣的文件架構javascript
·
├── utils
│ ├── actionTypes
│ ├── isPlainObject
│ ├── warning
│ └─
│
├── applyMiddleware
│
├── bindActionCreatorts
│
├── combineReducers
│
├── compose
│
├── createStore
│
├── index.js
│
└─
複製代碼
看起來文件比較少,因此,開始跟我阿寬,不對,跟着各路大哥們的總結,一塊兒看源碼吧。java
從小媽媽就告訴我,看源碼要從 index.js 入手,就我看來,index都做爲入口文件
,因此咱們去 index.js 中看一下代碼。簡單明瞭,我就很少說~git
import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes'
/* * 這是一個虛函數,用於檢查函數名稱是否已被縮小更改. * 若是這個函數被修改且 NODE_ENV !== 'production',警告用戶 */
function isCrushed() {}
if (
process.env.NODE_ENV !== 'production' &&
typeof isCrushed.name === 'string' &&
isCrushed.name !== 'isCrushed'
) {
warning('巴拉巴拉,要看提示就去看源碼哈')
}
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes
}
複製代碼
咱們回顧下以前說的 :redux 中最核心的 API 就是 —— createStore
, 如何使用呢 ?github
const store = createStore(reducers, preloadedState, enhance)
複製代碼
三個參數,reducers、preloadedState、enhance,源碼中也有對這三個參數給出瞭解釋redux
/** * 建立一個包含狀態樹的Redux存儲 * 更改store中數據的惟一方法是在其上調用 `dispatch()` * * 你的app中應該只有一個store,指定狀態樹的不一樣部分如何響應操做 * 你可使用 `combineReducers` 將幾個reducer組合成一個reducer函數 * * @param {Function} reducer 給定當前狀態樹和要處理的操做的函數,返回下一個狀態樹 * * @param {any} [preloadedState] 初始狀態. 你能夠選擇將其指定爲中的universal apps服務器狀態,或者還原之前序列化的用戶會話。 * 若是你使用 `combineReducers` 來產生 root reducer 函數,那麼它必須是一個與 `combineReducers` 鍵形狀相同的對象 * * @param {Function} [enhancer] store enhancer. 你能夠選擇指定它來加強store的第三方功能 * 好比 middleware、time travel、persistence, Redux附帶的惟一商店加強器是 `applyMiddleware()` * * @returns {Store} Redux Store,容許您讀取狀態,調度操做和訂閱更改。 */
複製代碼
看完了上面的解釋,(...谷歌翻譯好累啊), 咱們繼續往下看,咱們從Redux 官網中,能看到,Store Mehods 有四個方法,源碼中也有看獲得~api
import $$observable from 'symbol-observable'
import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject'
export default function createStore(reducer, preloadedState, enhancer) {
// ...
// 檢查一哈你的state和enhancer參數是否傳反
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
// 若是有傳入合法的enhance,則經過enhancer再調用一次createStore
if (typeof enhancer !== 'undefined') {
// enhancer 指望獲得的是一個函數
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
// reducer 指望獲得的是一個函數
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false // 是否正在分發事件
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
// 通常在action middleware 中常常會使用 `getState()`方法去獲取當前的state
function getState() {
if (isDispatching) {
throw new Error(
// ...
)
}
return currentState
}
// 源碼中這個函數的解釋太長了,我大概讀懂了,可是就不寫翻譯了哈,大概以下
// 註冊listener,同時返回一個取消事件註冊的方法。當調用store.dispatch的時候調用listener
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error(
'期待 listener 是一個函數'
)
}
if (isDispatching) {
throw new Error(
'在reducer執行時,您可能沒法調用store.subscribe(),若是你但願在更新store後收到通知,
請從組件訂閱並在回調中調用 store.getState() 以訪問最新狀態'
)
}
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if(!isSubscribed) {
return
}
if (isDispatching) {
throw new Error(
'在reducer執行時,你可能沒法取消訂閱store偵聽器'
)
}
isSubscribed = false
// 從 nextListeners 中去除掉當前 listener
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
// dispatch方法接收的action是個對象,而不是方法。dispatch()是觸發狀態變化的惟一方法。
// 這個對象實際上就是咱們自定義action的返回值,由於dispatch的時候,已經調用過咱們的自定義action了
// 更多詳情去官網看講解 : https://redux.js.org/api/store#example
function dispatch(action) {
if (!isPlainObject(action)) { // isPlainObject 最上邊 import 進來的
throw new Error(
'Actions 必須是一個普通對象'
)
}
if (typeof action.type === 'undefined') {
throw new Error(
'Actions 可能沒有未定義的「類型」屬性'
)
}
// 調用dispatch的時候只能一個個調用,經過dispatch判斷調用的狀態
if (isDispatching) {
throw new Error(
// ...
)
}
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = (currentListeners = nextListeners)
// 遍歷調用每一個 listener
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
// Replaces the reducer currently used by the store to calculate the state.
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error(
// ...
)
}
currentReducer = nextReducer
dispatch({
type: ActionTypes.REPLACE // ActionTypes 最上邊 import 進來的
})
}
// ...
// 當create store的時候,reducer會接受一個type爲ActionTypes.INIT的action,使reducer返回他們默認的state,這樣能夠快速的造成默認的state的結構
dispatch({
type: ActionTypes.INIT
})
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
複製代碼
是否是不多,不少英文註釋都去掉了,包括 throw new Error
我也沒寫出來,我更推薦的是,本身去看源碼!!!bash
彩蛋嘛,就是咱們來揭開上邊中提到的 actionTypes
和 isPlainObject
中的神祕面紗~服務器
actionTypes.js架構
/** * 這些是Redux保留的私有操做類型 * 對於任何未知 actions,你必須返回當前狀態 * 若是當前的 state 是 undefined,你必須返回初始state * 不要直接在代碼中引用這些操做類型 */
const randomString = () =>
Math.random()
.toString(36)
.substring(7)
.split('')
.join('.')
const ActionTypes = {
INIT: `@@redux/INIT${randomString()}`,
REPLACE: `@@redux/REPLACE${randomString()}`,
PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
}
export default ActionTypes
複製代碼
isPlainObject.jsapp
/** * 若是參數是普通對象,則返回true * Object.getPrototypeOf() 方法返回指定對象的原型,若是沒有繼承屬性,則返回 null */
export default function isPlainObject(obj) {
if (typeof obj !== 'object' || obj === null) return false
let proto = obj
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto
}
複製代碼
上一篇在這裏 : 《爲何redux要返回一個新的state引起的血案》
寫博客文章實在太累了,因此還有下一集,還有上邊若是有錯,請大哥們指出,說真,我也是去 github 看源碼,去官網看文檔,搜一些大哥們的文章,還要打開谷歌翻譯;
其實我寫的,也都是站在各位大哥的肩膀上去結合本身理解寫的總結,鍵盤俠就不要看了,求求大家了,大家太牛逼了,直接去看源碼吧,傳送門在底下~要不這也給你一個連接吧
還有關於爲何只貼代碼,是由於,直接看代碼是最直觀的!,好比一個美女,你要我告訴你,她有多麼的沉魚落雁、閉月羞花、國色天香...,仍是你直接看比較直觀?
《Redux 源碼》: github.com/reduxjs/red…
《Redux 官網》: redux.js.org/api/store#s…
《Object.getPrototypeOf() 》: developer.mozilla.org/en-US/docs/…