今天來(lái)分析一下react-redux源碼
react-redux所有代碼注釋地址:https://github.com/fangkyi03/react-redux.git
如果大家有問(wèn)題的話(huà) 可以加我QQ:469373256
寫(xiě)完這個(gè)文章我也是心力交瘁了 react-redux 一些數(shù)據(jù)都是放在閉包里面的 然后循環(huán)的調(diào)用有點(diǎn)蒙圈的感覺(jué)
- 接口預(yù)覽
react-redux只暴露了這幾個(gè)接口 接下來(lái) 將會(huì)按照代碼的執(zhí)行順序 進(jìn)行一一的分析
import Provider, { createProvider } from './components/Provider'
import connectAdvanced from './components/connectAdvanced'
import connect from './connect/connect'
export { Provider, createProvider, connectAdvanced, connect }
- Provider
先來(lái)看一下函數(shù)定義部分
export function createProvider(storeKey = 'store', subKey)
export default createProvider()
import Provider, { createProvider } from './components/Provider'
我們默認(rèn)如果直接使用react-redux中的Provider的話(huà) 默認(rèn)傳遞過(guò)去的數(shù)據(jù)是不可控制的 如果你想做一些自定義設(shè)置的話(huà) 建議選擇引入createProvider
- Provider 主體部分
export function createProvider(storeKey = 'store', subKey) {
如果subKey有值就使用傳入的值 否則使用store
所以最后的結(jié)果就是變成storeSubscription
const subscriptionKey = subKey || `${storeKey}Subscription`
class Provider extends Component {
react-redux使用context來(lái)實(shí)現(xiàn)父級(jí)對(duì)所有的子級(jí)傳遞數(shù)據(jù) 但是這個(gè)context已經(jīng)在16.3版本有了新的方法 這里就不多講了
首先 如果你要讓子獲取到父的數(shù)據(jù) 那么肯定得有一個(gè)入口 這個(gè)getChildContext就是子固定的入口 它會(huì)去判斷自身的ContextTypes是否有數(shù)據(jù) 如果有的話(huà) 就會(huì)直接調(diào)用父級(jí)的這個(gè)getChildContext來(lái)返回對(duì)應(yīng)的數(shù)據(jù)
getChildContext() {
return { [storeKey]: this[storeKey], [subscriptionKey]: null }
}
constructor(props, context) {
super(props, context)
this[storeKey] = props.store;
}
render() {
只允許渲染一個(gè)
return Children.only(this.props.children)
}
}
如果不是線(xiàn)上模式的話(huà)并且如果這個(gè)組件的父發(fā)生改變的話(huà) 將會(huì)出現(xiàn)錯(cuò)誤提醒
if (process.env.NODE_ENV !== 'production') {
Provider.prototype.componentWillReceiveProps = function (nextProps) {
if (this[storeKey] !== nextProps.store) {
warnAboutReceivingStore()
}
}
}
對(duì)于props組件類(lèi)型的定義
Provider.propTypes = {
store: storeShape.isRequired,
children: PropTypes.element.isRequired,
}
這里是要傳遞給子組件的數(shù)據(jù)類(lèi)型定義
Provider.childContextTypes = {
[storeKey]: storeShape.isRequired,
[subscriptionKey]: subscriptionShape,
}
return Provider
}
ok 這個(gè)組件就分析到此 沒(méi)有什么可以太多講的 需要注意的就是context 但是一般我們不會(huì)接觸到這個(gè)
- connect核心完整代碼
import connectAdvanced from '../components/connectAdvanced'
import shallowEqual from '../utils/shallowEqual'
import defaultMapDispatchToPropsFactories from './mapDispatchToProps'
import defaultMapStateToPropsFactories from './mapStateToProps'
import defaultMergePropsFactories from './mergeProps'
import defaultSelectorFactory from './selectorFactory'
/*
connect is a facade over connectAdvanced. It turns its args into a compatible
selectorFactory, which has the signature:
(dispatch, options) => (nextState, nextOwnProps) => nextFinalProps
connect passes its args to connectAdvanced as options, which will in turn pass them to
selectorFactory each time a Connect component instance is instantiated or hot reloaded.
selectorFactory returns a final props selector from its mapStateToProps,
mapStateToPropsFactories, mapDispatchToProps, mapDispatchToPropsFactories, mergeProps,
mergePropsFactories, and pure args.
The resulting final props selector is called by the Connect component instance whenever
it receives new props or store state.
*/
function match(arg, factories, name) {
for (let i = factories.length - 1; i >= 0; i--) {
const result = factories[i](arg)
if (result) return result
}
return (dispatch, options) => {
throw new Error(`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${options.wrappedComponentName}.`)
}
}
function strictEqual(a, b) { return a === b }
// createConnect with default args builds the 'official' connect behavior. Calling it with
// different options opens up some testing and extensibility scenarios
export function createConnect({
connectHOC = connectAdvanced,
mapStateToPropsFactories = defaultMapStateToPropsFactories,
mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
mergePropsFactories = defaultMergePropsFactories,
selectorFactory = defaultSelectorFactory
} = {}) {
return function connect(
mapStateToProps,
mapDispatchToProps,
mergeProps,
{
pure = true,
areStatesEqual = strictEqual,
areOwnPropsEqual = shallowEqual,
areStatePropsEqual = shallowEqual,
areMergedPropsEqual = shallowEqual,
...extraOptions
} = {}
) {
const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps')
const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')
return connectHOC(selectorFactory, {
// used in error messages
methodName: 'connect',
// used to compute Connect's displayName from the wrapped component's displayName.
getDisplayName: name => `Connect(${name})`,
// if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
shouldHandleStateChanges: Boolean(mapStateToProps),
// passed through to selectorFactory
initMapStateToProps,
initMapDispatchToProps,
initMergeProps,
pure,
areStatesEqual,
areOwnPropsEqual,
areStatePropsEqual,
areMergedPropsEqual,
// any extra options args can override defaults of connect or connectAdvanced
...extraOptions
})
}
}
export default createConnect()
- createConnect部分
export function createConnect({
connectHOC = connectAdvanced,
mapStateToPropsFactories = defaultMapStateToPropsFactories,
mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
mergePropsFactories = defaultMergePropsFactories,
selectorFactory = defaultSelectorFactory
} = {})
export default createConnect()
import connect from './connect/connect'
先來(lái)看一下函數(shù)定義 從上面的函數(shù)定義可以看到 其實(shí)是有暴露createConnect來(lái)進(jìn)行使用的 只不過(guò)默認(rèn)使用的是不傳參的createConnect導(dǎo)致全部都走了默認(rèn)值 但是其實(shí) 如果你想做一些自定義設(shè)置的話(huà) 可以直接調(diào)用createConnect來(lái)實(shí)現(xiàn)
- connect部分
return function connect(
mapStateToProps,
mapDispatchToProps,
mergeProps,
{
pure = true,
areStatesEqual = strictEqual,
areOwnPropsEqual = shallowEqual,
areStatePropsEqual = shallowEqual,
areMergedPropsEqual = shallowEqual,
...extraOptions
} = {}
)
這里其實(shí)就是我們平常在用的connect了 第四個(gè)參數(shù)一般用來(lái)傳入pure或者withRef來(lái)控制是否使用pure跟是否返回ref withRef等其他的參數(shù)都存在了extraOptions這個(gè)里面
- initProps初始化部分
const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps')
const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')
function match(arg, factories, name) {
for (let i = factories.length - 1; i >= 0; i--) {
const result = factories[i](arg)
if (result) return result
}
return (dispatch, options) => {
throw new Error(`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${options.wrappedComponentName}.`)
}
}
這里match傳入的factories 為對(duì)應(yīng)的判斷條件類(lèi)型是一個(gè)數(shù)組 只要你的判斷條件中 有任意一條符合條件 就直接拋出 否則返回 一個(gè)錯(cuò)誤
> * initMapStateToProps部分
這個(gè)根據(jù)mapStateToProps是否為undefined分為兩部分
whenMapStateToPropsIsFunction
whenMapStateToPropsIsMissing
initMapStateToProps
- wrapMapToPropsConstant 部分詳解
export function wrapMapToPropsConstant(getConstant) {
return function initConstantSelector(dispatch, options) {
const constant = getConstant(dispatch, options)
function constantSelector() { return constant }
constantSelector.dependsOnOwnProps = false
return constantSelector
}
}
wrapMapToPropsConstant(() => ({}))
從這個(gè)例子里面看 這里寫(xiě)這么多代碼其實(shí)是無(wú)意義的 但是 這個(gè)主要是為了留給后面使用
wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch))
- wrapMapToPropsFunc 部分詳解
export function getDependsOnOwnProps(mapToProps) {
return (mapToProps.dependsOnOwnProps !== null && mapToProps.dependsOnOwnProps !== undefined)
? Boolean(mapToProps.dependsOnOwnProps)
: mapToProps.length !== 1
}
// Used by whenMapStateToPropsIsFunction and whenMapDispatchToPropsIsFunction,
// this function wraps mapToProps in a proxy function which does several things:
//
// * Detects whether the mapToProps function being called depends on props, which
// is used by selectorFactory to decide if it should reinvoke on props changes.
//
// * On first call, handles mapToProps if returns another function, and treats that
// new function as the true mapToProps for subsequent calls.
//
// * On first call, verifies the first result is a plain object, in order to warn
// the developer that their mapToProps function is not returning a valid result.
//
export function wrapMapToPropsFunc(mapToProps, methodName) {
return function initProxySelector(dispatch, { displayName }) {
// 當(dāng)程序執(zhí)行到這里的時(shí)候 其實(shí)proxy并沒(méi)有被運(yùn)行 只是返回了一個(gè)proxy的聲明
// 會(huì)在handleFirstCall被進(jìn)行調(diào)用來(lái)初始化
// 因?yàn)閯偛懦绦蛞呀?jīng)走了一遍的關(guān)系 這里dependsOnOwnProps默認(rèn)會(huì)變成true
// 所以會(huì)走下面的那個(gè)proxy.mapToProps
const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
return proxy.dependsOnOwnProps
? proxy.mapToProps(stateOrDispatch, ownProps)
: proxy.mapToProps(stateOrDispatch)
}
// allow detectFactoryAndVerify to get ownProps
proxy.dependsOnOwnProps = true
proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {
// 當(dāng)程序走到這里的時(shí)候 會(huì)將mapToProps進(jìn)行復(fù)制 這邊要進(jìn)行運(yùn)算了 只要mapToProps返回的內(nèi)容不為function 并且等于object的話(huà) 程序就直接結(jié)束了
// 否則就會(huì)繼續(xù)賦值不斷的遍歷執(zhí)行 知道返回了object以后 才將最終的結(jié)果返回到最外層
proxy.mapToProps = mapToProps
proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
// 這里賦值以后 又會(huì)重新的走上面那個(gè)函數(shù) 但是因?yàn)楝F(xiàn)在用的是我們自己寫(xiě)的mapStateToProps函數(shù)了 所以不可能會(huì)有dependsOnOwnProps
// 所以這邊的dependsOnOwnProps永遠(yuǎn)會(huì)是false 因?yàn)閒alse的關(guān)系 所以會(huì)直接調(diào)用mapStateToProps(stateOrDispatch)
let props = proxy(stateOrDispatch, ownProps)
if (typeof props === 'function') {
// 只要返回的不是function并且等于object的話(huà) 這邊就結(jié)束了 直接返回新的數(shù)據(jù) 否則不停的深層執(zhí)行
proxy.mapToProps = props
proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
props = proxy(stateOrDispatch, ownProps)
}
if (process.env.NODE_ENV !== 'production')
verifyPlainObject(props, displayName, methodName)
return props
}
return proxy
}
}
之前我這一部分也沒(méi)有完全看懂 這里的設(shè)計(jì)非常巧妙 正常情況下 我們的mapStateToProps返回的就是一個(gè)普通的對(duì)象 但是mapStateToProps其實(shí)支持返回一個(gè)function 那么問(wèn)題就來(lái)了 因?yàn)槊總€(gè)人的書(shū)寫(xiě)習(xí)慣不一致的關(guān)系 所以我在mapStateToProps返回了一個(gè)閉包以后 要如何正常獲取數(shù)據(jù)呢 這句代碼的用處就是不斷的遍歷直到你返回的數(shù)據(jù)為一個(gè)object才放行
- initMapDispatchToProps部分
這里分成了三個(gè)部分 前面兩個(gè)部分 跟上面講的是一樣的就不多說(shuō)了 主要說(shuō)一下第三個(gè)部分
whenMapDispatchToPropsIsFunction = whenMapStateToPropsIsFunction
whenMapDispatchToPropsIsMissing = whenMapStateToPropsIsMissing
whenMapDispatchToPropsIsObject
- whenMapDispatchToPropsIsObject 部分詳解
export function whenMapDispatchToPropsIsObject(mapDispatchToProps) {
return (mapDispatchToProps && typeof mapDispatchToProps === 'object')
? wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch))
: undefined
}
export function wrapMapToPropsConstant(getConstant) {
return function initConstantSelector(dispatch, options) {
const constant = getConstant(dispatch, options)
function constantSelector() { return constant }
constantSelector.dependsOnOwnProps = false
return constantSelector
}
}
說(shuō)明:
假設(shè)現(xiàn)在的mapDispatchToProps是這樣的一個(gè)結(jié)構(gòu)
{
x:function(data){
return {type:'xxx',data}
}
}
經(jīng)過(guò)bindActionCreators以后就變成了
x:function(action){
dispatch(x(action))
}
在每個(gè)function的前面都外包了一層 使得可以做到自動(dòng)發(fā)起action的功能
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
function bindActionCreator(actionCreator, dispatch) {
return (...args) => dispatch(actionCreator(...args))
}
- initMergeProps
- whenMergePropsIsOmitted 如果為undefined的時(shí)候 載入
export function whenMergePropsIsOmitted(mergeProps) {
return (!mergeProps)
? () => defaultMergeProps
: undefined
}
export function defaultMergeProps(stateProps, dispatchProps, ownProps) {
return { ...ownProps, ...stateProps, ...dispatchProps }
}
這里不多說(shuō) 就是合并數(shù)據(jù)用的
- whenMergePropsIsFunction 如果mergeProps為function的時(shí)候
export function whenMergePropsIsFunction(mergeProps) {
return (typeof mergeProps === 'function')
? wrapMergePropsFunc(mergeProps)
: undefined
}
export function wrapMergePropsFunc(mergeProps) {
return function initMergePropsProxy(
dispatch, { displayName, pure, areMergedPropsEqual }
) {
let hasRunOnce = false
let mergedProps
return function mergePropsProxy(stateProps, dispatchProps, ownProps) {
const nextMergedProps = mergeProps(stateProps, dispatchProps, ownProps)
if (hasRunOnce) {
if (!pure || !areMergedPropsEqual(nextMergedProps, mergedProps))
mergedProps = nextMergedProps
} else {
hasRunOnce = true
mergedProps = nextMergedProps
if (process.env.NODE_ENV !== 'production')
verifyPlainObject(mergedProps, displayName, 'mergeProps')
}
return mergedProps
}
}
}
這部分后續(xù)補(bǔ)上 因?yàn)橛玫牟皇翘貏e的多
- connectHOC 部分
return connectHOC(selectorFactory, {
// used in error messages
methodName: 'connect',
// used to compute Connect's displayName from the wrapped component's displayName.
getDisplayName: name => `Connect(${name})`,
// if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
shouldHandleStateChanges: Boolean(mapStateToProps),
// passed through to selectorFactory
initMapStateToProps,
initMapDispatchToProps,
initMergeProps,
pure,
areStatesEqual,
areOwnPropsEqual,
areStatePropsEqual,
areMergedPropsEqual,
// any extra options args can override defaults of connect or connectAdvanced
...extraOptions
})
connectHOC中return了這個(gè)函數(shù) 這就是為什么我們的connect是
connect()()這樣調(diào)用的原因 這個(gè)一個(gè)高階組件 由function返回一個(gè)view
return function wrapWithConnect(WrappedComponent)
先看一個(gè)參數(shù)部分
const selectorFactoryOptions = {
...connectOptions,
getDisplayName,
methodName,
renderCountProp,
shouldHandleStateChanges,
storeKey,
withRef,
displayName,
wrappedComponentName,
WrappedComponent
}
this.initSelector()
this.initSubscription()
這兩行代碼 決定了整個(gè)的react-redux的運(yùn)行
- initSelector
initSelector() {
const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
this.selector = makeSelectorStateful(sourceSelector, this.store)
this.selector.run(this.props)
}
selectorFactory這里的這個(gè)參數(shù)是由外部的createConnect時(shí)傳遞進(jìn)來(lái)的 看一下這個(gè)定義
- selectorFactory
export default function finalPropsSelectorFactory(dispatch, {
initMapStateToProps,
initMapDispatchToProps,
initMergeProps,
...options
}) {
// 這里就會(huì)初始化wrapMapToPropsFunc中第一層的代碼 并且將里面的內(nèi)容返回繼續(xù)往下傳遞 具體可以看前面的說(shuō)明
const mapStateToProps = initMapStateToProps(dispatch, options)
const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
const mergeProps = initMergeProps(dispatch, options)
if (process.env.NODE_ENV !== 'production') {
verifySubselectors(mapStateToProps, mapDispatchToProps, mergeProps, options.displayName)
}
// 這里就比較關(guān)鍵了 如果你設(shè)置了pure以后 這邊走的其實(shí)是兩個(gè)完全不同的路線(xiàn)
// 先優(yōu)先看一下比較簡(jiǎn)單的部分
const selectorFactory = options.pure
? pureFinalPropsSelectorFactory
: impureFinalPropsSelectorFactory
return selectorFactory(
mapStateToProps,
mapDispatchToProps,
mergeProps,
dispatch,
options
)
- impureFinalPropsSelectorFactory
export function impureFinalPropsSelectorFactory(
mapStateToProps,
mapDispatchToProps,
mergeProps,
dispatch
) {
return function impureFinalPropsSelector(state, ownProps) {
return mergeProps(
mapStateToProps(state, ownProps),
mapDispatchToProps(dispatch, ownProps),
ownProps
)
}
}
這個(gè)函數(shù)的作用 就是調(diào)用mergerProps并返回最新的nextProps數(shù)據(jù) 用于進(jìn)行比對(duì) 這是針對(duì)沒(méi)有pure屬性的 如果有pure屬性 就會(huì)相對(duì)復(fù)雜一點(diǎn)了
- pureFinalPropsSelectorFactory
// 這里傳遞進(jìn)來(lái)的
// 實(shí)際上已經(jīng)是運(yùn)行了第一層代碼的返回函數(shù)了
// mapStateToProps,
// mapDispatchToProps,
// mergeProps,
export function pureFinalPropsSelectorFactory(
mapStateToProps,
mapDispatchToProps,
mergeProps,
dispatch,
{ areStatesEqual, areOwnPropsEqual, areStatePropsEqual }
) {
let hasRunAtLeastOnce = false
let state
let ownProps
let stateProps
let dispatchProps
let mergedProps
function handleFirstCall(firstState, firstOwnProps) {
// 首次運(yùn)行就比較簡(jiǎn)單 因?yàn)椴恍枰ケ葘?duì)任何的數(shù)據(jù) 也沒(méi)有任何的數(shù)據(jù)可以比對(duì)
// 所以這里只要直接拿數(shù)據(jù)就可以了
state = firstState
ownProps = firstOwnProps
stateProps = mapStateToProps(state, ownProps)
dispatchProps = mapDispatchToProps(dispatch, ownProps)
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
// 在這里將這個(gè)條件設(shè)置為真 下次就不會(huì)跳轉(zhuǎn)到首次運(yùn)行這邊了
hasRunAtLeastOnce = true
return mergedProps
}
function handleNewPropsAndNewState() {
// 執(zhí)行返回state
stateProps = mapStateToProps(state, ownProps)
// 如果dependsOnOwnProps為true返回新的props否則使用原有的
if (mapDispatchToProps.dependsOnOwnProps){
dispatchProps = mapDispatchToProps(dispatch, ownProps)
}
// 合并所有數(shù)據(jù)
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
return mergedProps
}
// 合并新的props
function handleNewProps() {
// 這部分可以看wrapMapToPropsFunc部分的說(shuō)明 有詳細(xì)解釋
// dependsOnOwnProps 正常情況
// 第一次執(zhí)行的時(shí)候 肯定是true 第二次是個(gè)fasle 因?yàn)楫?dāng)時(shí)的函數(shù)里面還沒(méi)有帶上這個(gè)屬性
// 第三次的話(huà)就是true了 這時(shí)候調(diào)用proxy的時(shí)候 這個(gè)屬性就有了
// 當(dāng)你正常的返回了一個(gè)object以后 你就帶上了這個(gè)屬性
// 那么當(dāng)你下次在進(jìn)行更新的時(shí)候就會(huì)直接獲取新的數(shù)據(jù)了
if (mapStateToProps.dependsOnOwnProps)
stateProps = mapStateToProps(state, ownProps)
if (mapDispatchToProps.dependsOnOwnProps)
dispatchProps = mapDispatchToProps(dispatch, ownProps)
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
return mergedProps
}
// 這里跟上面的一致
function handleNewState() {
const nextStateProps = mapStateToProps(state, ownProps)
const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps)
stateProps = nextStateProps
if (statePropsChanged)
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
return mergedProps
}
function handleSubsequentCalls(nextState, nextOwnProps) {
// 比對(duì)props跟state是否被更改
const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)
const stateChanged = !areStatesEqual(nextState, state)
state = nextState
ownProps = nextOwnProps
// 如果兩個(gè)都被更改就一起生成新的 否則就針對(duì)自身進(jìn)行生成
if (propsChanged && stateChanged) return handleNewPropsAndNewState()
if (propsChanged) return handleNewProps()
if (stateChanged) return handleNewState()
// 如果什么都沒(méi)有改變的話(huà) 則直接返回原有數(shù)據(jù)
return mergedProps
}
// hasRunAtLeastOnce判斷是否是首次運(yùn)行 如果是首次運(yùn)行就跳轉(zhuǎn)到handleFirstCall
return function pureFinalPropsSelector(nextState, nextOwnProps) {
return hasRunAtLeastOnce
? handleSubsequentCalls(nextState, nextOwnProps)
: handleFirstCall(nextState, nextOwnProps)
}
}
- 核心更新比對(duì)效驗(yàn)的部分
areStatesEqual = strictEqual,
areOwnPropsEqual = shallowEqual,
areStatePropsEqual = shallowEqual,
areMergedPropsEqual = shallowEqual,
從這里可以看到 基本上所有的更新 都是走shallowEqual這個(gè)的 針對(duì)這個(gè)來(lái)做一下分析
export default function shallowEqual(objA, objB) {
// 先判斷兩個(gè)對(duì)象是否一致 如果一致則返回true 認(rèn)為不需要更新
if (is(objA, objB)) return true
// 只要滿(mǎn)足null 或者 !== object 這兩個(gè)條件的任意一個(gè) 都返回false 認(rèn)為需要更新
if (typeof objA !== 'object' || objA === null ||
typeof objB !== 'object' || objB === null) {
return false
}
const keysA = Object.keys(objA)
const keysB = Object.keys(objB)
// 如果A B 是兩個(gè)對(duì)象 keys以后的長(zhǎng)度不一致 也返回false 認(rèn)為下需要更新
if (keysA.length !== keysB.length) return false
// 如果兩者長(zhǎng)度一致 在依次比對(duì)內(nèi)部數(shù)據(jù) 看是否一致
for (let i = 0; i < keysA.length; i++) {
// 如果objB里面不存在keysA屬性的話(huà) 直接返回fasle認(rèn)為需要更新了
// 或者如果相同數(shù)組下objA與objB不相等的話(huà) 也認(rèn)為需要更新
if (!hasOwn.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])) {
return false
}
}
// 除此情況外 都認(rèn)為不需要更新
return true
}
更新邏輯
其中sourceSelector這個(gè) 就是上面剛講到的那部分 每次執(zhí)行返回一個(gè)最新的nextProps并且還會(huì)在跟自身原有的selector.props比對(duì) 如果不一致就將
selector.shouldComponentUpdate = true
然后在后面步驟去參與更新
constructor =>initSelector =>makeSelectorStateful
先在構(gòu)造函數(shù)中執(zhí)行這個(gè)進(jìn)行初始化
this.initSelector()
this.initSubscription()
之后在initSelector中對(duì)于數(shù)據(jù)進(jìn)行初始化 這里很關(guān)鍵
const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
this.selector = makeSelectorStateful(sourceSelector, this.store)
this.selector.run(this.props)
這里負(fù)責(zé)比對(duì)數(shù)據(jù)sourceSelector就是上面講到的 會(huì)走對(duì)應(yīng)閉包里面的函數(shù) 用于返回一個(gè)最新的nextProps然后跟當(dāng)前的selector.props比對(duì) 如果不一樣的話(huà)就返回一個(gè)最新的 如果一樣就處理 也就是 一個(gè)false
selector.shouldComponentUpdate 這個(gè)會(huì)在每次render的時(shí)候 被設(shè)置為false
function makeSelectorStateful(sourceSelector, store) {
// wrap the selector in an object that tracks its results between runs.
const selector = {
run: function runComponentSelector(props) {
try {
const nextProps = sourceSelector(store.getState(), props)
if (nextProps !== selector.props || selector.error) {
selector.shouldComponentUpdate = true
selector.props = nextProps
selector.error = null
}
} catch (error) {
selector.shouldComponentUpdate = true
selector.error = error
}
}
}
return selector
}
- 接下來(lái)講一下Subscription監(jiān)聽(tīng)的部分
先來(lái)看一下在程序中的調(diào)用順序
this.initSelector()
this.initSubscription()
initSubscription() {
if (!shouldHandleStateChanges) return
// parentSub's source should match where store came from: props vs. context. A component
// connected to the store via props shouldn't use subscription from context, or vice versa.
// 這里有一點(diǎn)需要注意的就是propsMode 如果有數(shù)據(jù)的話(huà) 會(huì)從props取 沒(méi)有則從context取 正常情況下
// 簡(jiǎn)單來(lái)說(shuō) 我們知道context會(huì)被繼承下去 如果這里有獲取到數(shù)據(jù)的話(huà) 就會(huì)直接使用原有的 而不是現(xiàn)有的
// 并不是每次每個(gè)組件都使用了一個(gè)新的監(jiān)聽(tīng) 而是從父級(jí)開(kāi)始 如果父有了一個(gè)監(jiān)聽(tīng) 就使用父的 當(dāng)我這邊去進(jìn)行改變的時(shí)候
// 默認(rèn)其實(shí)會(huì)觸發(fā)父的監(jiān)聽(tīng) 在由父來(lái)回調(diào)當(dāng)前頁(yè)面的監(jiān)聽(tīng)函數(shù) 做到盡量少的創(chuàng)建一個(gè)監(jiān)聽(tīng) 實(shí)現(xiàn)公用
const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey]
this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this))
// `notifyNestedSubs` is duplicated to handle the case where the component is unmounted in
// the middle of the notification loop, where `this.subscription` will then be null. An
// extra null check every change can be avoided by copying the method onto `this` and then
// replacing it with a no-op on unmount. This can probably be avoided if Subscription's
// listeners logic is changed to not call listeners that have been unsubscribed in the
// middle of the notification loop.
// 這里綁定監(jiān)聽(tīng)需要用到的函數(shù)
// 當(dāng)你第一次執(zhí)行的時(shí)候 因?yàn)? // const nullListeners = { notify() {} }
// this.listeners = nullListeners
// 這個(gè)時(shí)候直接執(zhí)行是不會(huì)有任何效果的
this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription)
}
第一次初始化會(huì)在didMount以后被觸發(fā)
componentDidMount() {
if (!shouldHandleStateChanges) return
// componentWillMount fires during server side rendering, but componentDidMount and
// componentWillUnmount do not. Because of this, trySubscribe happens during ...didMount.
// Otherwise, unsubscription would never take place during SSR, causing a memory leak.
// To handle the case where a child component may have triggered a state change by
// dispatching an action in its componentWillMount, we have to re-run the select and maybe
// re-render.
// 在這里執(zhí)行第一次的監(jiān)聽(tīng) 這里僅僅還只是加入隊(duì)列中 并沒(méi)有執(zhí)行
this.subscription.trySubscribe()
// 這里計(jì)算一次返回最新的this.selector.shouldComponentUpdate
this.selector.run(this.props)
// 如果當(dāng)前需要更新的話(huà) 采取強(qiáng)制更新
if (this.selector.shouldComponentUpdate) this.forceUpdate()
}
trySubscribe() {
// 當(dāng)你執(zhí)行了這個(gè)函數(shù)以后
if (!this.unsubscribe) {
this.unsubscribe = this.parentSub
//
? this.parentSub.addNestedSub(this.onStateChange)
// 這里使用的是redux中提供的函數(shù) 在這里當(dāng)你每次dispatch的時(shí)候 就會(huì)被觸發(fā)到
: this.store.subscribe(this.onStateChange)
// 當(dāng)你走到這里的時(shí)候 listeners就被替換了 這個(gè)時(shí)候 才是真正可以使用監(jiān)聽(tīng)了
// 如果你不是從parent獲取更新的話(huà) 默認(rèn)這里使用redux來(lái)維護(hù)你的監(jiān)聽(tīng)
// 但是如果你有parent以后 是由內(nèi)部來(lái)進(jìn)行維護(hù)的
// 內(nèi)部調(diào)用的時(shí)候 其實(shí)會(huì)繼續(xù)走addNestedSub
// this.listeners.subscribe(listener)
// 并且在這里返回一個(gè)監(jiān)聽(tīng)
// 這個(gè)定義的變量也只會(huì)在有父級(jí)的時(shí)候 被使用到 因?yàn)樽陨硪灿锌赡艹蔀閯e人的父
// 所以這邊沒(méi)有做其他的判斷 采取全部都定義的方式
this.listeners = createListenerCollection()
}
}
ok 到這里為止 綁定就都完成了 剩下的就是等redux的store改變的時(shí)候 觸發(fā)更新了 來(lái)看一下更新部分的邏輯
- createListenerCollection部分
function createListenerCollection() {
// the current/next pattern is copied from redux's createStore code.
// TODO: refactor+expose that code to be reusable here?
let current = []
let next = []
return {
// 清楚當(dāng)前隊(duì)列
clear() {
next = CLEARED
current = CLEARED
},
//這里要注意 如果當(dāng)前下面沒(méi)有子的話(huà) 其實(shí)這里的代碼是無(wú)意義的
// 只有自身成為了父 并且有子視圖掛在下面這里才會(huì)執(zhí)行隊(duì)列
notify() {
const listeners = current = next
for (let i = 0; i < listeners.length; i++) {
listeners[i]()
}
},
// 獲取當(dāng)前的最新監(jiān)聽(tīng)數(shù)據(jù)
get() {
return next
},
subscribe(listener) {
let isSubscribed = true
// 如果不相等就將當(dāng)前的監(jiān)聽(tīng)添加到數(shù)組中
if (next === current) next = current.slice()
next.push(listener)
// 這里模仿了redux返回了一個(gè)卸載監(jiān)聽(tīng)的函數(shù)
return function unsubscribe() {
if (!isSubscribed || current === CLEARED) return
isSubscribed = false
if (next === current) next = current.slice()
next.splice(next.indexOf(listener), 1)
}
}
}
}
- react-redux render部分代碼
addExtraProps(props) {
if (!withRef && !renderCountProp && !(this.propsMode && this.subscription)) return props
// make a shallow copy so that fields added don't leak to the original selector.
// this is especially important for 'ref' since that's a reference back to the component
// instance. a singleton memoized selector would then be holding a reference to the
// instance, preventing the instance from being garbage collected, and that would be bad
const withExtras = { ...props }
if (withRef) withExtras.ref = this.setWrappedInstance
if (renderCountProp) withExtras[renderCountProp] = this.renderCount++
if (this.propsMode && this.subscription) withExtras[subscriptionKey] = this.subscription
return withExtras
}
render() {
const selector = this.selector
selector.shouldComponentUpdate = false
if (selector.error) {
throw selector.error
} else {
return createElement(WrappedComponent, this.addExtraProps(selector.props))
}
}
這里比較簡(jiǎn)單就是將參數(shù)復(fù)制過(guò)去而已
react-redux所有代碼注釋地址:https://github.com/fangkyi03/react-redux.git
ok react-redux所有代碼 到此全部玩不