深入浅出ーReduxソース
10438 ワード
目次、ステータスツリーを維持し、 を発行します. 1.1 dispatch .subscribe 1.3 get State 1.4 replacceReducer 1.5[$observable]:observable 、ミドルウェア 、ツール関数 3.1 compse 3.2 cobineReduces 3.3ビッドアクションクリエーター 付録 本文の前提条件を読み、Reduxの基本概念を知る.
雑談:
フレームを理解するには、まず分かります.
フレームの痛みは何ですか?
フロントエンドは状態管理(同期状態、非同期状態)が必要です.正直に言って、簡単にコードを書くのは状態管理ですが、これは私達の追求ですか?もちろんそうではありません.私たちは先端の状態を優雅に管理する必要があります.MVCアーキテクチャでは、データ(Model)----インターフェース(View)----インタラクティブ操作(Controller)に簡単にマッピングすることができる.
状態管理とは基本的にデータとインタラクティブ操作の管理です.Reduxは基本的にこのようなことをしました.
まとめ:
Reduxソースは基本的に二つの操作をしました.
1)状態ツリーのメンテナンス
2)ステータスのリリース購読
一、ステータスツリーを維持し、購読を発表する
上のredux基本原理のソースコードは、基本的に終了しました.上記以外にもいくつかのツール関数compose、cobineReducers、bindActioCreatorsの3つのツール関数を提供しています.
3.1 compse
composeは主に中間部品を組み合わせる時に中間部品を組み合わせます.結局は一つの関数しか受け入れられません.そして要求関数は順番に一つずつ実行します.
commbineReducersは主に複数のreducersを一つのreducersに組み合わせるために使われています.reduxを書いたのは一般的にContactainersを書いています.各containersはreducersがありますが、これらのreducersをどうやって組み合わせたらいいですか?commbineReducersがするのはこのことです.結局Redux要求は一つのStoreだけが一つのReducersです.
ビッドアクションクリエーターの役割は、dispatchを隠すために、dispatchを実行可能な関数に変えます.核心はビッドアクションクリエーターです.
どのように対象がPlainObjectかを判断します.自分で理解してください.書けなくなりました.もうだめです.テキストの原型チェーンをひっくり返します.
雑談:
フレームを理解するには、まず分かります.
フレームの痛みは何ですか?
フロントエンドは状態管理(同期状態、非同期状態)が必要です.正直に言って、簡単にコードを書くのは状態管理ですが、これは私達の追求ですか?もちろんそうではありません.私たちは先端の状態を優雅に管理する必要があります.MVCアーキテクチャでは、データ(Model)----インターフェース(View)----インタラクティブ操作(Controller)に簡単にマッピングすることができる.
状態管理とは基本的にデータとインタラクティブ操作の管理です.Reduxは基本的にこのようなことをしました.
まとめ:
Reduxソースは基本的に二つの操作をしました.
1)状態ツリーのメンテナンス
2)ステータスのリリース購読
一、ステータスツリーを維持し、購読を発表する
/**
* @param {Function} reducer:reducer ,actionType---->new state
* @param {any} preloadedState: store , , , merge
* @param {Function} enhancer: , action
* @returns {Store} : , Store , 、
*/
export default function createStore(reducer, preloadedState, enhancer) {
// , preloadedState enhancer ,
if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
throw new Error(
'It looks like you are passing several store enhancers to ' +
'createStore(). This is not supported. Instead, compose them ' +
'together to a single function'
)
}
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
/**
* , , ?
* @params {createStore}
* @params {reducer, preloadedState}
*/
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
/**
* , , Store
* @variable
* -currentReducer: reducer, reducer
* -currentState: , state , ,undefiend
* -currentListener: ,listener
* -nextListener:
* -isDispatching: , , , , , ,
*/
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
/**
* store
*/
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
1.1 dispatch/**
* Dispatches an action. store
*
* @param {Object} action ,plainObject
*
* @returns {Object} ,
*
* , , `dispatch()` , , plainObject
*
* , , ,
*/
function dispatch(action) {
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
1.2 subscribe/**
* , callback
* @param {Function} dispatch
* @returns {Function}
*/
function subscribe(listener) {
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
if (isDispatching) {
throw new Error(
'You may not unsubscribe from a store listener while the reducer is executing. ' +
'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
)
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
1.3 get State/**
* @returns {any}
*/
function getState() {
if (isDispatching) {
throw new Error(
'You may not call store.getState() while the reducer is executing. ' +
'The reducer has already received the state as an argument. ' +
'Pass it down from the top reducer instead of reading it from the store.'
)
}
return currentState
}
1.4 replacceReducer/**
* @param {Function} reducer,
* @returns {void}
*/
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
currentReducer = nextReducer
dispatch({ type: ActionTypes.REPLACE })
}
1.5[$observable]:observable/** observabla , , , Redux ,
* [Observable ](https://github.com/tc39/proposal-observable)
* [Observable ](https://distums.github.io/2017/03/19/observables-proposal-for-ecmascript/)
* subscribe :
*/
function observable() {
const outerSubscribe = subscribe
return {
subscribe(observer) {
if (typeof observer !== 'object' || observer === null) {
throw new TypeError('Expected the observer to be an object.')
}
function observeState() {
if (observer.next) {
observer.next(getState())
}
}
observeState()
const unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},
[$$observable]() {
return this
}
}
}
中間部品/**
* redux ,
* @param {...Function} middlewares
* @returns {Function} enchancer
* , reducer state ,
* return enhancer(createStore)(reducer, preloadedState)
*/
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
三、ツール関数上のredux基本原理のソースコードは、基本的に終了しました.上記以外にもいくつかのツール関数compose、cobineReducers、bindActioCreatorsの3つのツール関数を提供しています.
3.1 compse
composeは主に中間部品を組み合わせる時に中間部品を組み合わせます.結局は一つの関数しか受け入れられません.そして要求関数は順番に一つずつ実行します.
/**
* compose ,
* compose (f, g, h)===> (...args) => f(f(h(...args)))
* reduce ,
*/
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
3.2 commbineReducerscommbineReducersは主に複数のreducersを一つのreducersに組み合わせるために使われています.reduxを書いたのは一般的にContactainersを書いています.各containersはreducersがありますが、これらのreducersをどうやって組み合わせたらいいですか?commbineReducersがするのはこのことです.結局Redux要求は一つのStoreだけが一つのReducersです.
/**
* @param {Object} , reducers
*
* @returns {Function} reducers
* finalReducers , reducers , reducers( , )
*/
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
let unexpectedKeyCache
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}
return function combination(state = {}, action) {
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
3.3ビッドアクションクリエータービッドアクションクリエーターの役割は、dispatchを隠すために、dispatchを実行可能な関数に変えます.核心はビッドアクションクリエーターです.
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
しかし、多くのactionsがありますので、各actionのためにdispatchを隠すために、便利な操作が必要です.export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
const keys = Object.keys(actionCreators)
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
付録どのように対象がPlainObjectかを判断します.自分で理解してください.書けなくなりました.もうだめです.テキストの原型チェーンをひっくり返します.
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 git倉庫