###react-redux源码结构
-- index.js
-- utils - shallowEqual.js - storeShape.js - warning.js - wrapActionCreators.js
--components - Provider.js - connect.js
为了方便解读,先从基础方法开始
index.js 源码
import Provider from './components/Provider' // 导入Provider组件
import connect from './components/connect' // 导入connect组件
export { Provider, connect } // 将Provider, connect导出
utils 工具包源码
warning.js
/**
*
* 在console打印一条警告信息
*
* @param {String} 警告信息内容
* @returns {void} 返回值
*/
export default function warning(message) {
// 文件中指定规则(console)不验证
/* eslint-disable no-console */
if (typeof console !== 'undefined' && typeof console.error === 'function') {
console.error(message)
}
/* eslint-enable no-console */
try {
// 抛出异常并暂停执行
throw new Error(message)
/* eslint-disable no-empty */
} catch (e) {}
/* eslint-enable no-empty */
}
shallowEqual.js
// 判断objA 与 obj 是否相等
export default function shallowEqual(objA, objB) {
// 如果objA和objB是严格相等,结果相等
if (objA === objB) {
return true
}
// 获取 objA 和 objB 的所有属性
const keysA = Object.keys(objA)
const keysB = Object.keys(objB)
// 属性长度不相等,结果不相等
if (keysA.length !== keysB.length) {
return false
}
// 使用hasWon引用Object.prototype.hasOwnProperty方法
const hasOwn = Object.prototype.hasOwnProperty
// objB 中不含有 objA 中的属性,结果不相等
for (let i = 0; i < keysA.length; i++) {
if (!hasOwn.call(objB, keysA[i]) ||
objA[keysA[i]] !== objB[keysA[i]]) {
return false
}
}
return true
}
storeShape.js
import { PropTypes } from 'react'
// 导出store Prop 验证规则
export default PropTypes.shape({
subscribe: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
getState: PropTypes.func.isRequired
})
wrapActionCreators.js
// 导入 bindActionCreators 方法,此方法在后续redux源码会讲到
import { bindActionCreators } from 'redux'
// 将dispatch应用到actionCreators
export default function wrapActionCreators(actionCreators) {
return dispatch => bindActionCreators(actionCreators, dispatch)
}
components源码
Provider.js
// Provider 依赖react,导入react中元素
import { Component, PropTypes, Children } from 'react'
// 导入store验证规则以及警告提示方法
import storeShape from '../utils/storeShape'
import warning from '../utils/warning'
// 标识是否已经收到警告
let didWarnAboutReceivingStore = false
// 此方法主要用来警告当前的对象是否和传入的对象相同
function warnAboutReceivingStore() {
if (didWarnAboutReceivingStore) {
return
}
didWarnAboutReceivingStore = true
warning(
'<Provider> does not support changing `store` on the fly. ' +
'It is most likely that you see this error because you updated to ' +
'Redux 2.x and React Redux 2.x which no longer hot reload reducers ' +
'automatically. See https://github.com/reactjs/react-redux/releases/' +
'tag/v2.0.0 for the migration instructions.'
)
}
// Provider是一个内部组建
export default class Provider extends Component {
// 构造方法传入props和context
constructor(props, context) {
super(props, context)
this.store = props.store
}
// 返回从构造方法传递的store,将store传递给子孙component
getChildContext() {
return { store: this.store }
}
// 返回仅有的一个子元素,否则(没有子元素或超过一个子元素)
// 报错且不渲染任何东西。 这也说明Provider下必须只能是一个
// 子元素
render() {
return Children.only(this.props.children)
}
}
// 如果我们运行的环境是production,则还需要定义
// componentWillReceiveProps原型方法
if (process.env.NODE_ENV !== 'production') {
Provider.prototype.componentWillReceiveProps = function (nextProps) {
const { store } = this
const { store: nextStore } = nextProps
// 如果当前对象与传递的对象不一样,则发出警告
if (store !== nextStore) {
warnAboutReceivingStore()
}
}
}
Provider.propTypes = {
store: storeShape.isRequired,
children: PropTypes.element.isRequired
}
Provider.childContextTypes = {
store: storeShape.isRequired
}
connect.js react-redux真正的主角上场了,游戏才刚开始,游戏结束还有59分钟。
// 这个就是导入一系列react、utils中的方法
import { Component, createElement } from 'react'
import storeShape from '../utils/storeShape'
import shallowEqual from '../utils/shallowEqual'
import wrapActionCreators from '../utils/wrapActionCreators'
import warning from '../utils/warning'
import isPlainObject from 'lodash/isPlainObject'
import hoistStatics from 'hoist-non-react-statics'
import invariant from 'invariant'
// 一看default,嘿,connect方法中mapStateToProps、mapDispatchToProps的默认值。
// mapStateToProps默认为一个空对象
// mapDispatchToProps默认为包含一个dispatch方法的对象
// mergeProps主要是合并上两个和其他
const defaultMapStateToProps = state => ({}) // eslint-disable-line no-unused-vars
const defaultMapDispatchToProps = dispatch => ({ dispatch })
const defaultMergeProps = (stateProps, dispatchProps, parentProps) => ({
...parentProps,
...stateProps,
...dispatchProps
})
// 显示组件名称
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
// 运行指定方法并捕获异常,并将错误放在errorObject对象中的value中
let errorObject = { value: null }
function tryCatch(fn, ctx) {
try {
return fn.apply(ctx)
} catch (e) {
errorObject.value = e
return errorObject
}
}
// Helps track hot reloading.
let nextVersion = 0
// connect是一个高阶函数, 它包含四个参数,并返回一个
// 生产Component的函数,它将真正的Component作为参数传入
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
// mapStateToProps第一个参数(function)
// 返回bool值,使用传入mapStateToProps判断是否可订阅
const shouldSubscribe = Boolean(mapStateToProps)
//mapStateToProps默认值,即{}
const mapState = mapStateToProps || defaultMapStateToProps
let mapDispatch
// mapDispatchToProps为第二个参数(function | object),
// mapDispatchToProps可传递的类型为function 或许 对象
// 如果传递的类型为function
if (typeof mapDispatchToProps === 'function') {
mapDispatch = mapDispatchToProps
// 如果没有传递mapDispatchToProps,则为默认
} else if (!mapDispatchToProps) {
mapDispatch = defaultMapDispatchToProps
} else {
// 只要敢调用wrapActionCreators,它就返回参数含有dispatch的方法。
// wrapActionCreators方法中应用了bindActionCreators
mapDispatch = wrapActionCreators(mapDispatchToProps)
}
// 第三个参数(function), 如果指定这个参数
// mapStateToProps() 与 mapDispatchToProps()的
// 执行结果和组件自身的 props 将传入到这个回调函数中
const finalMergeProps = mergeProps || defaultMergeProps
// 第四个参数(Object)
// pure为true表示执行shouldComponentUpdate并且浅比较mergeProps
// withRef为true表示connector会保存一个对被包装组件实例的引用,
// 该引用通过 getWrappedInstance() 方法获得
const { pure = true, withRef = false } = options
// 判断是否检查 mergeProps
const checkMergedEquals = pure && finalMergeProps !== defaultMergeProps
// 保持版本号增长(非生产环境下).
const version = nextVersion++
// connect的返回值,WrappedComponent是React中的一个容器组件
return function wrapWithConnect(WrappedComponent) {
// 格式化顶级组件的名称
const connectDisplayName = `Connect(${getDisplayName(WrappedComponent)})`
// 校验State中的props 是否为纯对象
function checkStateShape(props, methodName) {
// 判断对象否为纯粹的对象字面量,即用来检测一个对象是否由{}或new Object()创建
if (!isPlainObject(props)) {
warning(
`${methodName}() in ${connectDisplayName} must return a plain object. ` +
`Instead received ${props}.`
)
}
}
// 合并stateProps、dispatchProps、parentProps
function computeMergedProps(stateProps, dispatchProps, parentProps) {
const mergedProps = finalMergeProps(stateProps, dispatchProps, parentProps)
// 不为生产环境时才执行
if (process.env.NODE_ENV !== 'production') {
checkStateShape(mergedProps, 'mergeProps')
}
return mergedProps
}
// 定义Connect组件
class Connect extends Component {
// 在接收到新的 props 或者 state,将要渲染之前调用。
// 该方法在初始化渲染的时候不会调用
// 如果 shouldComponentUpdate 返回 false,则 render() 将不会执行
shouldComponentUpdate() {
return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged
}
// 构造方法
constructor(props, context) {
super(props, context)
this.version = version
//从祖先Component处获得store
this.store = props.store || context.store
// props 或 context没有存放store,
invariant(this.store,
`Could not find "store" in either the context or ` +
`props of "${connectDisplayName}". ` +
`Either wrap the root component in a <Provider>, ` +
`or explicitly pass "store" as a prop to "${connectDisplayName}".`
)
// 从store获取state并成为当前对象的state
const storeState = this.store.getState()
this.state = { storeState }
this.clearCache()
}
computeStateProps(store, props) {
// 如果finalMapStateToProps不存在,表示还没有执行过configureFinalMapState方法
if (!this.finalMapStateToProps) {
return this.configureFinalMapState(store, props)
}
// 获取store中的state, 并执行finalMapStateToProps
// 如果StateProps依赖OwnProps,需要传递props
const state = store.getState()
const stateProps = this.doStatePropsDependOnOwnProps ?
this.finalMapStateToProps(state, props) :
this.finalMapStateToProps(state)
if (process.env.NODE_ENV !== 'production') {
checkStateShape(stateProps, 'mapStateToProps')
}
return stateProps
}
// 1. 调用mapState,即connect第一个参数,返回包含
// 2. 判断是否为 function
// 3. 判断ffinalMapStateToProps参数个数,长度为1,代表不依赖ownProps, 否则反之
configureFinalMapState(store, props) {
const mappedState = mapState(store.getState(), props)
const isFactory = typeof mappedState === 'function'
this.finalMapStateToProps = isFactory ? mappedState : mapState
this.doStatePropsDependOnOwnProps = this.finalMapStateToProps.length !== 1
if (isFactory) {
return this.computeStateProps(store, props)
}
if (process.env.NODE_ENV !== 'production') {
checkStateShape(mappedState, 'mapStateToProps')
}
return mappedState
}
computeDispatchProps(store, props) {
// 如果没有调用过configureFinalMapDispatch, 则先调
// 用configureFinalMapDispatch
if (!this.finalMapDispatchToProps) {
return this.configureFinalMapDispatch(store, props)
}
// 获取store中的dispatch, 并执行finalMapDispatchToProps,返回值为对象
// 如果DispatchProps依赖OwnProps,需要传递props
const { dispatch } = store
const dispatchProps = this.doDispatchPropsDependOnOwnProps ?
this.finalMapDispatchToProps(dispatch, props) :
this.finalMapDispatchToProps(dispatch)
if (process.env.NODE_ENV !== 'production') {
checkStateShape(dispatchProps, 'mapDispatchToProps')
}
// 返回最终dispatchProps
return dispatchProps
}
configureFinalMapDispatch(store, props) {
// 调用mapDispatch()方法, mapDisPatch方法返回一个含有dispatch[也可能含有props]参数的function
const mappedDispatch = mapDispatch(store.dispatch, props)
// 如果返回的是函数,isFactory为true。
const isFactory = typeof mappedDispatch === 'function'
// isFactory为true,返回mappedDispatch, 否则返回默认值
this.finalMapDispatchToProps = isFactory ? mappedDispatch : mapDispatch
// 判断finalMapDispatchToProps参数个数,长度为1,代表不依赖ownProps, 否则反之
this.doDispatchPropsDependOnOwnProps = this.finalMapDispatchToProps.length !== 1
// mappedDispatch === 'function'成立,调用computeDispatchProps
if (isFactory) {
return this.computeDispatchProps(store, props)
}
if (process.env.NODE_ENV !== 'production') {
checkStateShape(mappedDispatch, 'mapDispatchToProps')
}
return mappedDispatch
}
// 是否需要更新StateProps
updateStatePropsIfNeeded() {
const nextStateProps = this.computeStateProps(this.store, this.props)
if (this.stateProps && shallowEqual(nextStateProps, this.stateProps)) {
return false
}
this.stateProps = nextStateProps
return true
}
// 是否需要更新DispatchProps
updateDispatchPropsIfNeeded() {
const nextDispatchProps = this.computeDispatchProps(this.store, this.props)
if (this.dispatchProps && shallowEqual(nextDispatchProps, this.dispatchProps)) {
return false
}
this.dispatchProps = nextDispatchProps
return true
}
// 是否需要更新MergeProps
updateMergedPropsIfNeeded() {
const nextMergedProps = computeMergedProps(this.stateProps, this.dispatchProps, this.props)
if (this.mergedProps && checkMergedEquals && shallowEqual(nextMergedProps, this.mergedProps)) {
return false
}
this.mergedProps = nextMergedProps
return true
}
// 是否已经被订阅,订阅返回true
isSubscribed() {
return typeof this.unsubscribe === 'function'
}
// 执行订阅,并使用handleChange处理回调
// 先自动调用handleChange一次
trySubscribe() {
if (shouldSubscribe && !this.unsubscribe) {
this.unsubscribe = this.store.subscribe(this.handleChange.bind(this))
this.handleChange()
}
}
// 取消订阅
tryUnsubscribe() {
if (this.unsubscribe) {
this.unsubscribe()
this.unsubscribe = null
}
}
// 初始化渲染执行之后立刻调用一次
// 渲染之后执行订阅
componentDidMount() {
this.trySubscribe()
}
// 组件接收到新的 props 的时候调用。在初始化渲染的时候,该方法不会调用。
// 判断传递的Props是否与当前props相等
componentWillReceiveProps(nextProps) {
if (!pure || !shallowEqual(nextProps, this.props)) {
this.haveOwnPropsChanged = true
}
}
// 在组件从 DOM 中移除的时候立刻被调用
// 取消订阅 、清除缓存
componentWillUnmount() {
this.tryUnsubscribe()
this.clearCache()
}
// reset变量及引用
clearCache() {
this.dispatchProps = null
this.stateProps = null
this.mergedProps = null
this.haveOwnPropsChanged = true
this.hasStoreStateChanged = true
this.haveStatePropsBeenPrecalculated = false
this.statePropsPrecalculationError = null
this.renderedElement = null
this.finalMapDispatchToProps = null
this.finalMapStateToProps = null
}
// handle回调实现
handleChange() {
// 如果被取消订阅,终止执行。
if (!this.unsubscribe) {
return
}
// 比较当前state是否store中的state相同
// 相同停止执行
const storeState = this.store.getState()
const prevStoreState = this.state.storeState
if (pure && prevStoreState === storeState) {
return
}
if (pure && !this.doStatePropsDependOnOwnProps) {
const haveStatePropsChanged = tryCatch(this.updateStatePropsIfNeeded, this)
if (!haveStatePropsChanged) {
return
}
if (haveStatePropsChanged === errorObject) {
this.statePropsPrecalculationError = errorObject.value
}
this.haveStatePropsBeenPrecalculated = true
}
// 重新设置state
this.hasStoreStateChanged = true
this.setState({ storeState })
}
// 获取被包装组件实例的引用
// 如果没有设置withRef为true,
// 执行invariant
getWrappedInstance()
invariant(withRef,
`To access the wrapped instance, you need to specify ` +
`{ withRef: true } as the fourth argument of the connect() call.`
)
return this.refs.wrappedInstance
}
render() {
// 解构所需变量
const {
haveOwnPropsChanged,
hasStoreStateChanged,
haveStatePropsBeenPrecalculated,
statePropsPrecalculationError,
renderedElement
} = this
// reset变量
this.haveOwnPropsChanged = false
this.hasStoreStateChanged = false
this.haveStatePropsBeenPrecalculated = false
this.statePropsPrecalculationError = null
// 如果有错误存在statePropsPrecalculationError中,抛出错误
if (statePropsPrecalculationError) {
throw statePropsPrecalculationError
}
let shouldUpdateStateProps = true
let shouldUpdateDispatchProps = true
// 允许浅比较并且renderdElement存在,说明只需刷新
if (pure && renderedElement) {
// 判断是否需要更新state
shouldUpdateStateProps = hasStoreStateChanged || (
haveOwnPropsChanged && this.doStatePropsDependOnOwnProps
)
// 判断是否需要更新dispatch
shouldUpdateDispatchProps =
haveOwnPropsChanged && this.doDispatchPropsDependOnOwnProps
}
let haveStatePropsChanged = false
let haveDispatchPropsChanged = false
if (haveStatePropsBeenPrecalculated) {
haveStatePropsChanged = true
} else if (shouldUpdateStateProps) {
haveStatePropsChanged = this.updateStatePropsIfNeeded()
}
if (shouldUpdateDispatchProps) {
haveDispatchPropsChanged = this.updateDispatchPropsIfNeeded()
}
let haveMergedPropsChanged = true
// 如果haveStatePropsChanged、haveDispatchPropsChanged、
// haveOwnPropsChanged其中一个被改变
if (
haveStatePropsChanged ||
haveDispatchPropsChanged ||
haveOwnPropsChanged
) {
haveMergedPropsChanged = this.updateMergedPropsIfNeeded()
} else {
haveMergedPropsChanged = false
}
if (!haveMergedPropsChanged && renderedElement) {
return renderedElement
}
// 如果withRef为true,则保存一个对被包装组件实例的引用:
// ref: 'wrappedInstance' 是此组建创建的引用
if (withRef) {
this.renderedElement = createElement(WrappedComponent, {
...this.mergedProps,
ref: 'wrappedInstance'
})
} else {
this.renderedElement = createElement(WrappedComponent,
this.mergedProps
)
}
return this.renderedElement
}
}
Connect.displayName = connectDisplayName
Connect.WrappedComponent = WrappedComponent
Connect.contextTypes = {
store: storeShape
}
Connect.propTypes = {
store: storeShape
}
if (process.env.NODE_ENV !== 'production') {
Connect.prototype.componentWillUpdate = function componentWillUpdate() {
if (this.version === version) {
return
}
// We are hot reloading!
this.version = version
this.trySubscribe()
this.clearCache()
}
}
// 这才是衔接redux 与 react 的关键, 将 WrappedComponent 中
// 除去关键字的所有的key和Symbols赋给 Connect (react 和 js关键字)
// [hoist-non-react-statics源码](https://github.com/mridgway/hoist-non-react-statics/blob/master/index.js)
return hoistStatics(Connect, WrappedComponent)
}
}
参考: http://zhenhua-lee.github.io/react/redux.html