热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

你想要的——redux源码剖析

人人好,本日给人人带来的是redux(v3.6.0)的源码剖析~起首是redux的github地点点我接下来我们看看redux在项目中的简朴应用,平常我们都从最简朴的最先入手哈备注

人人好,本日给人人带来的是redux(v3.6.0)的源码剖析~

起首是redux的github地点 点我

接下来我们看看redux在项目中的简朴应用,平常我们都从最简朴的最先入手哈

备注:例子中连系的是react举行应用,固然redux不仅仅能连系react,还能连系市面上其他大多数的框架,这也是它比较流弊的处所

起首是建立一个store

import React from 'react'
import { render } from 'react-dom'
// 起首我们必需先导入redux中的createStore要领,用于建立store
// 导入applyMiddleware要领,用于应用中间件
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
// 导入redux的中间件thunk
import thunk from 'redux-thunk'
// 导入redux的中间件createLogger
import { createLogger } from 'redux-logger'
// 我们还必需本身定义reducer函数,用于根据我们传入的action来访问新的state
import reducer from './reducers'
import App from './containers/App'
// 建立寄存中间件数组
const middleware = [ thunk ]
if (process.env.NODE_ENV !== 'production') {
middleware.push(createLogger())
}
// 挪用createStore要领来建立store,传入的参数分别是reducer和应用中间件的函数
const store = createStore(
reducer,
applyMiddleware(...middleware)
)
// 将store作为属性传入,如许在每一个子组件中就都可以猎取这个store实例,然后应用store的要领
render(


,
document.getElementById('root')
)

接下来我们看看reducer是怎样定义的

// 起首我们导入redux中的combineReducers要领
import { combineReducers } from 'redux'
// 导入actions,这个非必需,然则引荐这么做
import {
SELECT_REDDIT, INVALIDATE_REDDIT,
REQUEST_POSTS, RECEIVE_POSTS
} from '../actions'
// 接下来这个两个要领selectedReddit,postsByReddit就是reducer要领
// reducer要领担任根据传入的action的范例,返回新的state,这里可以传入默许的state
const selectedReddit = (state = 'reactjs', action) => {
switch (action.type) {
case SELECT_REDDIT:
return action.reddit
default:
return state
}
}
const posts = (state = {
isFetching: false,
didInvalidate: false,
items: []
}, action) => {
switch (action.type) {
case INVALIDATE_REDDIT:
return {
...state,
didInvalidate: true
}
case REQUEST_POSTS:
return {
...state,
isFetching: true,
didInvalidate: false
}
case RECEIVE_POSTS:
return {
...state,
isFetching: false,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
}
default:
return state
}
}
const postsByReddit = (state = { }, action) => {
switch (action.type) {
case INVALIDATE_REDDIT:
case RECEIVE_POSTS:
case REQUEST_POSTS:
return {
...state,
[action.reddit]: posts(state[action.reddit], action)
}
default:
return state
}
}
// 末了我们经由历程combineReducers这个要领,将一切的reducer要领兼并成一个要领,也就是rootReducer要领
const rootReducer = combineReducers({
postsByReddit,
selectedReddit
})
// 导出这个rootReducer要领
export default rootReducer

接下来看看action的定义,实在action就是一个对象,对象中商定有一个必要的属性type,和一个非必要的属性payload;type代表了action的范例,指清楚明了这个action对state修正的企图,而payload则是传入一些分外的数据供reducer应用

export const REQUEST_POSTS = 'REQUEST_POSTS'
export const RECEIVE_POSTS = 'RECEIVE_POSTS'
export const SELECT_REDDIT = 'SELECT_REDDIT'
export const INVALIDATE_REDDIT = 'INVALIDATE_REDDIT'
export const selectReddit = reddit => ({
type: SELECT_REDDIT,
reddit
})
export const invalidateReddit = reddit => ({
type: INVALIDATE_REDDIT,
reddit
})
export const requestPosts = reddit => ({
type: REQUEST_POSTS,
reddit
})
export const receivePosts = (reddit, json) => ({
type: RECEIVE_POSTS,
reddit,
posts: json.data.children.map(child => child.data),
receivedAt: Date.now()
})
const fetchPosts = reddit => dispatch => {
dispatch(requestPosts(reddit))
return fetch(`https://www.reddit.com/r/${reddit}.json`)
.then(respOnse=> response.json())
.then(json => dispatch(receivePosts(reddit, json)))
}
const shouldFetchPosts = (state, reddit) => {
const posts = state.postsByReddit[reddit]
if (!posts) {
return true
}
if (posts.isFetching) {
return false
}
return posts.didInvalidate
}
export const fetchPostsIfNeeded = reddit => (dispatch, getState) => {
if (shouldFetchPosts(getState(), reddit)) {
return dispatch(fetchPosts(reddit))
}
}

以上就是redux最简朴的用法,接下来我们就来看看redux源码内里详细是怎样完成的吧

起首我们看看全部redux项目的目次构造,从目次中我们可以看出,redux的项目源码实在比较简朴

《你想要的——redux源码剖析》

接下来就从进口文件index.js最先看吧,这个文件实在没有完成什么实质性的功用,只是导出了redux所供应的才能

// 进口文件
// 起首引入响应的模块,详细模块的内容后续会详细剖析
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'
/*
* This is a dummy function to check if the function name has been altered by minification.
* If the function has been minified and NODE_ENV !== 'production', warn the user.
*/
function isCrushed() {}
if (
process.env.NODE_ENV !== 'production' &&
typeof isCrushed.name === 'string' &&
isCrushed.name !== 'isCrushed'
) {
warning(
'You are currently using minified code outside of NODE_ENV === \'production\'. ' +
'This means that you are running a slower development build of Redux. ' +
'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' +
'or DefinePlugin for webpack (http://stackoverflow.com/questions/30030031) ' +
'to ensure you have the correct code for your production build.'
)
}
// 导出响应的功用
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose
}

紧接着,我们就来看看redux中一个重要的文件,createStore.js。这个文件用于建立store

// 建立store的文件,供应了redux中store的一切内置的功用,也是redux中比较重要的一个文件
// 起首引入响应的模块
import isPlainObject from 'lodash/isPlainObject'
import $$observable from 'symbol-observable'
/**
* These are private action types reserved by Redux.
* For any unknown actions, you must return the current state.
* If the current state is undefined, you must return the initial state.
* Do not reference these action types directly in your code.
*/
// 定义了有个内部应用的ActionType
export const ActiOnTypes= {
INIT: '@@redux/INIT'
}
/**
* Creates a Redux store that holds the state tree.
* The only way to change the data in the store is to call `dispatch()` on it.
*
* There should only be a single store in your app. To specify how different
* parts of the state tree respond to actions, you may combine several reducers
* into a single reducer function by using `combineReducers`.
*
* @param {Function} reducer A function that returns the next state tree, given
* the current state tree and the action to handle.
*
* @param {any} [preloadedState] The initial state. You may optionally specify it
* to hydrate the state from the server in universal apps, or to restore a
* previously serialized user session.
* If you use `combineReducers` to produce the root reducer function, this must be
* an object with the same shape as `combineReducers` keys.
*
* @param {Function} [enhancer] The store enhancer. You may optionally specify it
* to enhance the store with third-party capabilities such as middleware,
* time travel, persistence, etc. The only store enhancer that ships with Redux
* is `applyMiddleware()`.
*
* @returns {Store} A Redux store that lets you read the state, dispatch actions
* and subscribe to changes.
*/
// 导出建立store的要领
// 这个要领吸收三个参数,分别是 reducer,预先加载的state,以及功用加强函数enhancer
export default function createStore(reducer, preloadedState, enhancer) {
// 调解参数,假如没有传入预先加载的state,而且第二个参数是一个函数的话,则把第二个参数为功用加强函数enhancer
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
// 推断enhancer必需是一个函数
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 这是一个很重要的处置惩罚,它将createStore要领作为参数传入enhancer函数,而且实行enhancer
// 这里主如果供应给redux中间件的应用,以此来到达加强全部redux流程的结果
// 经由历程这个函数,也给redux供应了无穷多的可能性
return enhancer(createStore)(reducer, preloadedState)
}
// reducer必需是一个函数,不然报错
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
// 将传入的reducer缓存到currentReducer变量中
let currentReducer = reducer
// 将传入的preloadedState缓存到currentState变量中
let currentState = preloadedState
// 定义当前的监听者行列
let currentListeners = []
// 定义下一个轮回的监听者行列
let nextListeners = currentListeners
// 定义一个推断是不是在dispatch的标志位
let isDispatching = false
// 推断是不是能实行下一次监听行列
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
// 这里是将当前监听行列经由历程拷贝的情势赋值给下次监听行列,如许做是为了防备在当前行列实行的时刻会影响到本身,所以拷贝了一份副本
nextListeners = currentListeners.slice()
}
}
/**
* Reads the state tree managed by the store.
*
* @returns {any} The current state tree of your application.
*/
// 猎取当前的state
function getState() {
return currentState
}
/**
* Adds a change listener. It will be called any time an action is dispatched,
* and some part of the state tree may potentially have changed. You may then
* call `getState()` to read the current state tree inside the callback.
*
* You may call `dispatch()` from a change listener, with the following
* caveats:
*
* 1. The subscriptions are snapshotted just before every `dispatch()` call.
* If you subscribe or unsubscribe while the listeners are being invoked, this
* will not have any effect on the `dispatch()` that is currently in progress.
* However, the next `dispatch()` call, whether nested or not, will use a more
* recent snapshot of the subscription list.
*
* 2. The listener should not expect to see all state changes, as the state
* might have been updated multiple times during a nested `dispatch()` before
* the listener is called. It is, however, guaranteed that all subscribers
* registered before the `dispatch()` started will be called with the latest
* state by the time it exits.
*
* @param {Function} listener A callback to be invoked on every dispatch.
* @returns {Function} A function to remove this change listener.
*/
// 往监听行列内里去增加监听者
function subscribe(listener) {
// 监听者必需是一个函数
if (typeof listener !== 'function') {
throw new Error('Expected listener to be a function.')
}
// 声明一个变量来标记是不是已subscribed,经由历程闭包的情势被缓存
let isSubscribed = true
// 建立一个当前currentListeners的副本,赋值给nextListeners
ensureCanMutateNextListeners()
// 将监听者函数push到nextListeners中
nextListeners.push(listener)
// 返回一个作废监听的函数
// 道理很简朴就是从将当前函数从数组中删除,应用的是数组的splice要领
return function unsubscribe() {
if (!isSubscribed) {
return
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
/**
* Dispatches an action. It is the only way to trigger a state change.
*
* The `reducer` function, used to create the store, will be called with the
* current state tree and the given `action`. Its return value will
* be considered the **next** state of the tree, and the change listeners
* will be notified.
*
* The base implementation only supports plain object actions. If you want to
* dispatch a Promise, an Observable, a thunk, or something else, you need to
* wrap your store creating function into the corresponding middleware. For
* example, see the documentation for the `redux-thunk` package. Even the
* middleware will eventually dispatch plain object actions using this method.
*
* @param {Object} action A plain object representing “what changed”. It is
* a good idea to keep actions serializable so you can record and replay user
* sessions, or use the time travelling `redux-devtools`. An action must have
* a `type` property which may not be `undefined`. It is a good idea to use
* string constants for action types.
*
* @returns {Object} For convenience, the same action object you dispatched.
*
* Note that, if you use a custom middleware, it may wrap `dispatch()` to
* return something else (for example, a Promise you can await).
*/
// redux中经由历程dispatch一个action,来触发对store中的state的修正
// 参数就是一个action
function dispatch(action) {
// 这里推断一下action是不是是一个纯对象,假如不是则抛出毛病
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
// action中必须要有type属性,不然抛出毛病
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
// 假如上一次dispatch还没完毕,则不能继承dispatch下一次
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
// 将isDispatching设置为true,示意当次dispatch最先
isDispatching = true
// 应用传入的reducer函数处置惩罚state和action,返回新的state
// 引荐不直接修正原有的currentState
currentState = currentReducer(currentState, action)
} finally {
// 当次的dispatch完毕
isDispatching = false
}
// 每次dispatch完毕以后,就实行监听行列中的监听函数
// 将nextListeners赋值给currentListeners,保证下一次实行ensureCanMutateNextListeners要领的时刻会从新拷贝一个新的副本
// 简朴粗犷的应用for轮回实行
const listeners = currentListeners = nextListeners
for (let i = 0; i const listener = listeners[i]
listener()
}
// 末了返回action
return action
}
/**
* Replaces the reducer currently used by the store to calculate the state.
*
* You might need this if your app implements code splitting and you want to
* load some of the reducers dynamically. You might also need this if you
* implement a hot reloading mechanism for Redux.
*
* @param {Function} nextReducer The reducer for the store to use instead.
* @returns {void}
*/
// replaceReducer要领,望文生义就是替代当前的reducer处置惩罚函数
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
currentReducer = nextReducer
dispatch({ type: ActionTypes.INIT })
}
/**
* Interoperability point for observable/reactive libraries.
* @returns {observable} A minimal observable of state changes.
* For more information, see the observable proposal:
* https://github.com/tc39/proposal-observable
*/
// 这个函数平常来讲用不到,他是合营其他特性的框架或编程头脑来应用的如rx.js,感兴趣的朋侪可以自行进修
// 这里就不多做引见
function observable() {
const outerSubscribe = subscribe
return {
/**
* The minimal observable subscription method.
* @param {Object} observer Any object that can be used as an observer.
* The observer object should have a `next` method.
* @returns {subscription} An object with an `unsubscribe` method that can
* be used to unsubscribe the observable from the store, and prevent further
* emission of values from the observable.
*/
subscribe(observer) {
if (typeof observer !== 'object') {
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
}
}
}
// When a store is created, an "INIT" action is dispatched so that every
// reducer returns their initial state. This effectively populates
// the initial state tree.
// dispatch一个初始化的action
dispatch({ type: ActionTypes.INIT })
// 末了返回这个store的一切才能
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}

接下来我们看看combineReducers.js这个文件,一般我们会用它来兼并我们的reducer要领

这个文件用于兼并多个reducer,然后返回一个根reducer

由于store中只允许有一个reducer函数,所以当我们须要举行模块拆分的时刻,就必须要用到这个要领

// 一最先先导入响应的函数
import { ActionTypes } from './createStore'
import isPlainObject from 'lodash/isPlainObject'
import warning from './utils/warning'
// 猎取UndefinedState的毛病信息
function getUndefinedStateErrorMessage(key, action) {
const actiOnType= action && action.type
const actiOnName= (actionType && `"${actionType.toString()}"`) || 'an action'
return (
`Given action ${actionName}, reducer "${key}" returned undefined. ` +
`To ignore an action, you must explicitly return the previous state. ` +
`If you want this reducer to hold no value, you can return null instead of undefined.`
)
}
function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) {
// 猎取reducers的一切key
const reducerKeys = Object.keys(reducers)
const argumentName = action && action.type === ActionTypes.INIT ?
'preloadedState argument passed to createStore' :
'previous state received by the reducer'
// 当reducers对象是一个空对象的话,返回正告案牍
if (reducerKeys.length === 0) {
return (
'Store does not have a valid reducer. Make sure the argument passed ' +
'to combineReducers is an object whose values are reducers.'
)
}
// state必需是一个对象
if (!isPlainObject(inputState)) {
return (
`The ${argumentName} has unexpected type of "` +
({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
`". Expected argument to be an object with the following ` +
`keys: "${reducerKeys.join('", "')}"`
)
}
// 推断state中是不是有reducer没有的key,由于redux对state分模块的时刻,是根据reducer来分别的
const unexpectedKeys = Object.keys(inputState).filter(key =>
!reducers.hasOwnProperty(key) &&
!unexpectedKeyCache[key]
)
unexpectedKeys.forEach(key => {
unexpectedKeyCache[key] = true
})
if (unexpectedKeys.length > 0) {
return (
`Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
`"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
`Expected to find one of the known reducer keys instead: ` +
`"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
)
}
}
// assertReducerShape函数,检测当碰到位置action的时刻,reducer是不是会返回一个undefined,假如是的话则抛出毛病
// 接收一个reducers对象
function assertReducerShape(reducers) {
// 遍历这个reducers对象
Object.keys(reducers).forEach(key => {
const reducer = reducers[key]
// 猎取reducer函数在处置惩罚当state是undefined,actionType为初始默许type的时刻返回的值
const initialState = reducer(undefined, { type: ActionTypes.INIT })
// 假如这个值是undefined,则抛出毛病,由于初始state不该该是undefined
if (typeof initialState === 'undefined') {
throw new Error(
`Reducer "${key}" returned undefined during initialization. ` +
`If the state passed to the reducer is undefined, you must ` +
`explicitly return the initial state. The initial state may ` +
`not be undefined. If you don't want to set a value for this reducer, ` +
`you can use null instead of undefined.`
)
}
// 当碰到一个不知道的action的时刻,reducer也不能返回undefined,不然也会抛出报错
const type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.')
if (typeof reducer(undefined, { type }) === 'undefined') {
throw new Error(
`Reducer "${key}" returned undefined when probed with a random type. ` +
`Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` +
`namespace. They are considered private. Instead, you must return the ` +
`current state for any unknown actions, unless it is undefined, ` +
`in which case you must return the initial state, regardless of the ` +
`action type. The initial state may not be undefined, but can be null.`
)
}
})
}
/**
* Turns an object whose values are different reducer functions, into a single
* reducer function. It will call every child reducer, and gather their results
* into a single state object, whose keys correspond to the keys of the passed
* reducer functions.
*
* @param {Object} reducers An object whose values correspond to different
* reducer functions that need to be combined into one. One handy way to obtain
* it is to use ES6 `import * as reducers` syntax. The reducers may never return
* undefined for any action. Instead, they should return their initial state
* if the state passed to them was undefined, and the current state for any
* unrecognized action.
*
* @returns {Function} A reducer function that invokes every reducer inside the
* passed object, and builds a state object with the same shape.
*/
// 导出combineReducers要领,接收一个参数reducers对象
export default function combineReducers(reducers) {
// 猎取reducers对象的key值
const reducerKeys = Object.keys(reducers)
// 定义一个终究要返回的reducers对象
const finalReducers = {}
// 遍历这个reducers对象的key
for (let i = 0; i // 缓存每一个key值
const key = reducerKeys[i]
if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
warning(`No reducer provided for key "${key}"`)
}
}
// 响应key的值是个函数,则将改函数缓存到finalReducers中
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
// 猎取finalReducers的一切的key值,缓存到变量finalReducerKeys中
const finalReducerKeys = Object.keys(finalReducers)
let unexpectedKeyCache
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}
// 定义一个变量,用于缓存毛病对象
let shapeAssertionError
try {
// 做毛病处置惩罚,概况看背面assertReducerShape要领
// 重要就是检测,
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertiOnError= e
}
return function combination(state = {}, action) {
// 假如有毛病,则抛出毛病
if (shapeAssertionError) {
throw shapeAssertionError
}
if (process.env.NODE_ENV !== 'production') {
// 猎取正告提醒
const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache)
if (warningMessage) {
warning(warningMessage)
}
}
// 定义一个变量来示意state是不是已被转变
let hasChanged = false
// 定义一个变量,来缓存转变后的state
const nextState = {}
// 最先遍历finalReducerKeys
for (let i = 0; i // 猎取有用的reducer的key值
const key = finalReducerKeys[i]
// 根据key值猎取对应的reducer函数
const reducer = finalReducers[key]
// 根据key值猎取对应的state模块
const previousStateForKey = state[key]
// 实行reducer函数,猎取响应模块的state
const nextStateForKey = reducer(previousStateForKey, action)
// 假如猎取的state是undefined,则抛出毛病
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
// 将猎取到的新的state赋值给新的state对应的模块,key则为当前reducer的key
nextState[key] = nextStateForKey
// 判读state是不是发作转变
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
// 假如state发作转变则返回新的state,不然返回本来的state
return hasChanged ? nextState : state
}
}

接下来我们在看看bindActionCreators.js这个文件

起首先熟悉actionCreators,简朴来讲就是建立action的要领,redux的action是一个对象,而我们常常应用一些函数来建立这些对象,则这些函数就是actionCreators

而这个文件完成的功用,是根据绑定的actionCreator,来完成自动dispatch的功用


import warning from './utils/warning'
// 关于每一个actionCreator要领,实行以后都邑取得一个action
// 这个bindActionCreator要领,会返回一个可以自动实行dispatch的要领
function bindActionCreator(actionCreator, dispatch) {
return (...args) => dispatch(actionCreator(...args))
}
/**
* Turns an object whose values are action creators, into an object with the
* same keys, but with every function wrapped into a `dispatch` call so they
* may be invoked directly. This is just a convenience method, as you can call
* `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
*
* For convenience, you can also pass a single function as the first argument,
* and get a function in return.
*
* @param {Function|Object} actionCreators An object whose values are action
* creator functions. One handy way to obtain it is to use ES6 `import * as`
* syntax. You may also pass a single function.
*
* @param {Function} dispatch The `dispatch` function available on your Redux
* store.
*
* @returns {Function|Object} The object mimicking the original object, but with
* every action creator wrapped into the `dispatch` call. If you passed a
* function as `actionCreators`, the return value will also be a single
* function.
*/
// 对外暴露这个bindActionCreators要领
export default function bindActionCreators(actionCreators, dispatch) {
// 假如传入的actionCreators参数是个函数,则直接挪用bindActionCreator要领
if (typeof actiOnCreators=== 'function') {
return bindActionCreator(actionCreators, dispatch)
}
// 毛病处置惩罚
if (typeof actionCreators !== 'object' || actiOnCreators=== null) {
throw new Error(
`bindActionCreators expected an object or a function, instead received ${actiOnCreators=== null ? 'null' : typeof actionCreators}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
)
}
// 假如actionCreators是一个对象,则猎取对象中的key
const keys = Object.keys(actionCreators)
// 定义一个缓存对象
const boundActiOnCreators= {}
// 遍历actionCreators的每一个key
for (let i = 0; i // 猎取每一个key
const key = keys[i]
// 根据每一个key猎取特定的actionCreator要领
const actiOnCreator= actionCreators[key]
// 假如actionCreator是一个函数,则直接挪用bindActionCreator要领,将返回的匿名函数缓存到boundActionCreators对象中
if (typeof actiOnCreator=== 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
} else {
warning(`bindActionCreators expected a function actionCreator for key '${key}', instead received type '${typeof actionCreator}'.`)
}
}
// 末了返回boundActionCreators对象
// 用户猎取到这个对象后,可拿出对象中的每一个key的对应的值,也就是各个匿名函数,实行匿名函数就可以完成dispatch功用
return boundActionCreators
}

接下来我们看看applyMiddleware.js这个文件,这个文件让redux有着无穷多的可能性。为何这么说呢,你往下看就知道了

// 这个文件的代码逻辑实在很简朴
// 起首导入compose函数,等一下我们会详细剖析这个compose函数
import compose from './compose'
/**
* Creates a store enhancer that applies middleware to the dispatch method
* of the Redux store. This is handy for a variety of tasks, such as expressing
* asynchronous actions in a concise manner, or logging every action payload.
*
* See `redux-thunk` package as an example of the Redux middleware.
*
* Because middleware is potentially asynchronous, this should be the first
* store enhancer in the composition chain.
*
* Note that each middleware will be given the `dispatch` and `getState` functions
* as named arguments.
*
* @param {...Function} middlewares The middleware chain to be applied.
* @returns {Function} A store enhancer applying the middleware.
*/
// 接下来导出applyMiddleware这个要领,这个要领也是我们常常用来作为createStore中enhance参数的一个要领
export default function applyMiddleware(...middlewares) {
// 起首先返回一个匿名函数,有无发明这个函数跟createStore很类似啊
// 没错实在他就是我们的之前看到的createStore
return (createStore) => (reducer, preloadedState, enhancer) => {
// 起首用本来的createStore建立一个store,并把它缓存起来
const store = createStore(reducer, preloadedState, enhancer)
// 猎取store中原始的dispatch要领
let dispatch = store.dispatch
// 定一个实行链数组
let chain = []
// 缓存原有store中getState和dispatch要领
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
// 实行每一个中间件函数,并将middlewareAPI作为参数传入,取得一个实行链数组
chain = middlewares.map(middleware => middleware(middlewareAPI))
// 将实行链数组传入compose要领中,并马上实行返回的要领取得末了包装事后的dispatch
// 这个历程简朴来讲就是,每一个中间件都邑接收一个store.dispatch要领,然后基于这个要领举行包装,然后返回一个新的dispatch
// 这个新的dispatch又作为参数传入下一个中间件函数,然后有举行包装。。。一向轮回这个历程,直到末了取得一个终究的dispatch
dispatch = compose(...chain)(store.dispatch)
// 返回一个store对象,并将新的dispatch要领掩盖原有的dispatch要领
return {
...store,
dispatch
}
}
}

看到这里,实在你已看完了大部分redux的内容,末了我们看看上述文件中应用到的compose要领是怎样完成的。

翻开compose.js,我们发明实在完成体式格局就是应用es5中数组的reduce要领来完成这类结果的

/**
* Composes single-argument functions from right to left. The rightmost
* function can take multiple arguments as it provides the signature for
* the resulting composite function.
*
* @param {...Function} funcs The functions to compose.
* @returns {Function} A function obtained by composing the argument functions
* from right to left. For example, compose(f, g, h) is identical to doing
* (...args) => f(g(h(...args))).
*/
export default function compose(...funcs) {
// 推断函数数组是不是为空
if (funcs.length === 0) {
return arg => arg
}
// 假如函数数组只要一个元素,则直接实行
if (funcs.length === 1) {
return funcs[0]
}
// 不然,就应用reduce要领实行每一个中间件函数,并将上一个函数的返回作为下一个函数的参数
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

哈哈,以上就是本日给人人分享的redux源码剖析~愿望人人可以喜好咯


推荐阅读
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 本文讨论了如何在codeigniter中识别来自angularjs的请求,并提供了两种方法的代码示例。作者尝试了$this->input->is_ajax_request()和自定义函数is_ajax(),但都没有成功。最后,作者展示了一个ajax请求的示例代码。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • 本文介绍了brain的意思、读音、翻译、用法、发音、词组、同反义词等内容,以及脑新东方在线英语词典的相关信息。还包括了brain的词汇搭配、形容词和名词的用法,以及与brain相关的短语和词组。此外,还介绍了与brain相关的医学术语和智囊团等相关内容。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
author-avatar
手机用户2502921293
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有