react-router-redux源码
1.index.js
// 作为外部syncHistoryWithStore接口方法
// 绑定store.dispatch方法引起的state中路由状态变更到影响浏览器location变更
// 绑定浏览器location变更触发store.dispatch,更新state中路由状态
// 返回当前的histroy、绑定方法listen(dispatch方法触发时执行,以绑定前的路由状态为参数)、解绑函数unsubscribe
export syncHistoryWithStore from './sync'
// routerReducer监听路由变更子reducer,通过redux的combineReducers复合多个reducer后使用
export { LOCATION_CHANGE, routerReducer } from './reducer'
// 构建actionCreater,作为外部push、replace、go、goBack、goForward方法的接口,通常不直接使用
export {
CALL_HISTORY_METHOD,
push, replace, go, goBack, goForward,
routerActions
} from './actions'
// 构建route中间件,用于分发action,触发路径跳转等事件
// import {applyMiddleware, compose, createStore} from 'redux'
// import {routerMiddleware} from 'react-router-redux'
// import thunk from 'redux-thunk'
// createStore(reducer,initialState,
// compose( applyMiddleware([thunk, routerMiddleware(history)]) )
// );
export routerMiddleware from './middleware'2.middleware.js
import { CALL_HISTORY_METHOD } from './actions'
// 构建route中间件,用于分发action,触发路径跳转等事件,作为外部的routerMiddleware接口
// 使用方式为
// import {applyMiddleware, compose, createStore} from 'redux'
// import {routerMiddleware} from 'react-router-redux'
// import thunk from 'redux-thunk'
// createStore(reducer,initialState,
// compose( applyMiddleware([thunk, routerMiddleware(history)]) )
// );
//
// history可以是客户端hashHistroy(url散列变化),服务器端boswerHistroy(路径、查询参数变化)
// 通过hashHistroy、boswerHistroy触发期望的事件,即hashHistroy、boswerHistroy方法执行
// method是hashHistroy、boswerHistroy的接口方法,包含push、replace、go、goBack、goForward方法
// 问题:hashHistroy、boswerHistroy不使用redux.store.dispatch方法触发事件,是否会影响state???
// redux-logger插件需要在react-router-redux插件挂载前挂载,方能打印action???
export default function routerMiddleware(history) {
return () => next => action => {
if (action.type !== CALL_HISTORY_METHOD) {
return next(action)
}
const { payload: { method, args } } = action
history[method](...args)
}
}3.reducer.js
export const LOCATION_CHANGE = '@@router/LOCATION_CHANGE'
const initialState = {
locationBeforeTransitions: null
}
// 监听路由变更子reducer,通过redux的combineReducers复合多个reducer后使用,作为外部routerReducer接口
// 提示redux使用过程中,可通过子组件模块中注入reducer,再使用combineReducers复合多个reducer
// 最后使用replaceReducer方法更新当前store的reducer,意义是构建reducer拆解到各个子模块中
export function routerReducer(state = initialState, { type, payload } = {}) {
if (type === LOCATION_CHANGE) {
return { ...state, locationBeforeTransitions: payload }
}
return state
}4.actions.js
export const CALL_HISTORY_METHOD = '@@router/CALL_HISTORY_METHOD'
function updateLocation(method) {
return (...args) => ({// 返回actionCreater
type: CALL_HISTORY_METHOD,// route事件标识,避免和用于定义的action冲突
payload: { method, args }// method系hashHistroy、boswerHistroy对外接口方法名,args为参数
})
}
// 返回actionCreater,作为外部push、replace、go、goBack、goForward方法的接口,通常不直接使用
export const push = updateLocation('push')
export const replace = updateLocation('replace')
export const go = updateLocation('go')
export const goBack = updateLocation('goBack')
export const goForward = updateLocation('goForward')
export const routerActions = { push, replace, go, goBack, goForward }5.sync.js
import { LOCATION_CHANGE } from './reducer'
// 默认state.routing存取route变更状态数据
const defaultSelectLocationState = state => state.routing
// 作为外部syncHistoryWithStore接口方法
// 绑定store.dispatch方法引起的state中路由状态变更到影响浏览器location变更
// 绑定浏览器location变更触发store.dispatch,更新state中路由状态
// 返回当前的histroy、绑定方法listen(dispatch方法触发时执行,以绑定前的路由状态为参数)、解绑函数unsubscribe
export default function syncHistoryWithStore(history, store, {
// 约定redux.store.state中哪个属性用于存取route变更状态数据
selectLocationState = defaultSelectLocationState,
// store中路由状态变更是否引起浏览器location改变
adjustUrlOnReplay = true
} = {}) {
// 确保redux.store.state中某个属性绑定了route变更状态
if (typeof selectLocationState(store.getState()) === 'undefined') {
throw new Error(
'Expected the routing state to be available either as `state.routing` ' +
'or as the custom expression you can specify as `selectLocationState` ' +
'in the `syncHistoryWithStore()` options. ' +
'Ensure you have added the `routerReducer` to your store\'s ' +
'reducers via `combineReducers` or whatever method you use to isolate ' +
'your reducers.'
)
}
let initialLocation// 初始化route状态数据
let isTimeTraveling// 浏览器页面location.url改变过程中标识,区别页面链接及react-router-redux变更location两种情况
let unsubscribeFromStore// 移除store.listeners中,因路由状态引起浏览器location变更函数
let unsubscribeFromHistory// 移除location变更引起路由状态更新函数
let currentLocation// 记录上一个当前路由状态数据
// 获取路由事件触发后路由状态,或者useInitialIfEmpty为真值获取初始化route状态,或者undefined
const getLocationInStore = (useInitialIfEmpty) => {
const locationState = selectLocationState(store.getState())
// locationState.locationBeforeTransitions为真值时,跳转路由事件未发生
return locationState.locationBeforeTransitions ||
(useInitialIfEmpty ? initialLocation : undefined)
}
// 初始化route状态数据
initialLocation = getLocationInStore()
// adjustUrlOnReplay为真值时,store数据改变事件dispatch发生后,浏览器页面更新location???
if (adjustUrlOnReplay) {
// 由store中路由状态改变情况,更新浏览器location
const handleStoreChange = () => {
// 获取路由事件触发后路由状态,或者初始路由状态
const locationInStore = getLocationInStore(true)
if (currentLocation === locationInStore || initialLocation === locationInStore) {
return
}
// 浏览器页面location.url改变过程中标识,区别页面链接及react-router-redux变更location两种情况
isTimeTraveling = true
// 记录上一个当前路由状态数据
currentLocation = locationInStore
// store数据改变后,浏览器页面更新location???
history.transitionTo({
...locationInStore,
action: 'PUSH'
})
isTimeTraveling = false
}
// 绑定事件,完成功能为,dispatch方法触发store中路由状态改变时,更新浏览器location
unsubscribeFromStore = store.subscribe(handleStoreChange)
// 初始化设置路由状态时引起页面location改变
handleStoreChange()
}
// 页面链接变更浏览器location,触发store.dispatch变更store中路由状态
const handleLocationChange = (location) => {
// react-router-redux引起浏览器location变更过程中,无效;页面链接变更,有效
if (isTimeTraveling) {
return
}
currentLocation = location
if (!initialLocation) {
initialLocation = location
if (getLocationInStore()) {
return
}
}
store.dispatch({
type: LOCATION_CHANGE,
payload: location
})
}
// hashHistory、boswerHistory监听浏览器location变更,触发store.dispatch变更store中路由状态
unsubscribeFromHistory = history.listen(handleLocationChange)
// support history 3.x
// 初始化更新store中路由状态
if(history.getCurrentLocation) {
handleLocationChange(history.getCurrentLocation())
}
// The enhanced history uses store as source of truth
return {
...history,
// store中dispatch方法触发时,绑定执行函数listener,以绑定前的路由状态为参数
listen(listener) {
// 绑定前的路由状态
let lastPublishedLocation = getLocationInStore(true)
let unsubscribed = false// 确保listener在解绑后不执行
const unsubscribeFromStore = store.subscribe(() => {
const currentLocation = getLocationInStore(true)
if (currentLocation === lastPublishedLocation) {
return
}
lastPublishedLocation = currentLocation
if (!unsubscribed) {
listener(lastPublishedLocation)
}
})
listener(lastPublishedLocation)
return () => {
unsubscribed = true
unsubscribeFromStore()
}
},
// 解绑函数,包括location到store的handleLocationChange、store到location的handleStoreChange
unsubscribe() {
if (adjustUrlOnReplay) {
unsubscribeFromStore()
}
unsubscribeFromHistory()
}
}
} 相关推荐
码代码的陈同学 2020-10-14
瓜牛呱呱 2020-11-12
柳木木的IT 2020-11-04
yifouhu 2020-11-02
lei0 2020-11-02
源码zanqunet 2020-10-26
lukezhong 2020-10-14
clh0 2020-09-18
changcongying 2020-09-17
星辰大海的路上 2020-09-13
abfdada 2020-08-26
mzy000 2020-08-24
shenlanse 2020-08-18
zhujiangtaotaise 2020-08-18
xiemanR 2020-08-17