1.Router.js
Router.js模块用于监听hashChange、popState事件,通过当前页面url更新Router组件的state,state形式为{location,routes,params,components},其中location为当前页面的路径数据,routes为当前页面路径下被激活的Route、IndexRoute、Redirect、IndexRedirect元素,params为当前页面的路径数据变量,components为当前页面下待渲染的UI组件。
UI组件借由RouterContent.js模块完成,通过在Router组件的render方法中调用React.createElement(RouterContent,props)实现渲染。Router的state数据{location,components}将作为RouterContent组件的props,因此路径变更时将引起RouterContent组件的props变更,而props的变更又将引起RouterContent组件的重绘。
'use strict';
exports.__esModule = true;
// 类似jquery.extend,将带靠背对象浅拷贝给目标对象
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source,key)) {
target[key] = source[key];
}
}
}
return target;
};
// node模块;invariant(condition,format,a,b,c,d,e,f),condition为否值时,以a,f替换报错模板字符串format,抛出错误
// _invariant2.default引用_invariant,即node模块invariant
var _invariant = require('invariant');
var _invariant2 = _interoprequiredefault(_invariant);
var _react = require('react');
var _react2 = _interoprequiredefault(_react);
// 创建transitionManager对象,用于监听、取消监听popstate、hashChange事件
var _createTransitionManager2 = require('./createTransitionManager');
var _createTransitionManager3 = _interoprequiredefault(_createTransitionManager2);
// props属性校验
var _InternalPropTypes = require('./InternalPropTypes');
// 负责渲染Router下子Route挂载的组件,由Router的state数据获取到激活的Route组件及其components待挂载的组件
// 通过底层的Route组件向上遍历父Route,创建待挂载渲染的props.component|components组件元素reactElement实现
var _RouterContext = require('./RouterContext');
var _RouterContext2 = _interoprequiredefault(_RouterContext);
// 提供一系列方法,目的是将Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
var _RouteUtils = require('./RouteUtils');
// 提供createRouterObject、assignRouterState方法
// createRouterObject(history,transitionManager,state)方法
// 创建复合对象,含有histroy绑定路由事件、获取路由数据、跳转路由相关方法
// 以及setRouteLeaveHook、isActive方法
// 及location、params、routes属性(同Router组件当前state的相关属性等值)
// assignRouterState(router,_ref)拷贝_ref的location、params、routes属性给router
// 次参_ref通常是Router组件当前的state
var _RouterUtils = require('./RouterUtils');
// 封装warning模块,目的是使含'deprecated'的警告文案只提示一次
var _routerWarning = require('./routerWarning');
var _routerWarning2 = _interoprequiredefault(_routerWarning);
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// obj的属性或方法在keys中也有,不予拷贝
function _objectWithoutProperties(obj,keys) {
var target = {};
for (var i in obj) {
if (keys.indexOf(i) >= 0) continue;
if (!Object.prototype.hasOwnProperty.call(obj,i)) continue;
target[i] = obj[i];
}
return target;
};
var _React$PropTypes = _react2.default.PropTypes,func = _React$PropTypes.func,object = _React$PropTypes.object;
// var props={
// history,// hashHistory、broswerHistroy、memoryHistory内含绑定路由事件、获取路由数据、跳转路由相关方法
// onError,// 页面跳转发生错误时,捕获错误并处理,设置时默认不报错
// onUpdate,// 页面跳转成功、组件重绘后,调用onUpdate方法
// children,// 子Route组件
// routes,// 同children等同,页面路由Route组件设置
// render,// 决定Route中components组件渲染机制,默认调用react.createElement
// createElement,// 负责Route下待挂载组件的渲染createElement(component,props);
// matchContext???
// }
// <Router {...props}><Route/><Router/>
// Router通过props.render方法调用RouterContent模块访问props.children即Route组件渲染Route.components组件
// <Router {...props}><Route/><Router/>书写方式Route组件不渲染的根由???
// Router模块内部通过createTransitionManager模块创建this.transitionManager对象用于管理路由事件
// 路由事件触发时,更新state={routes,location,component}形式,重新渲染组件
// 通过RouterUtils模块创建this.router对象用于管理路由事件、访问路由数据,向下传递给RouterContent渲染模块用
var Router = _react2.default.createClass({
displayName: 'Router',propTypes: {
history: object,// hashHistory、broswerHistroy、memoryHistory内含绑定路由事件、获取路由数据、跳转路由相关方法
children: _InternalPropTypes.routes,// 子Route组件
routes: _InternalPropTypes.routes,// 同props.children,子Route组件
render: func,// Route中components组件渲染机制,render({router,components,createElement,props})
// props含有部分Router组件接收到的props
createElement: func,props);
onError: func,// 报错时执行函数
onUpdate: func,// 路径变更时执行函数
// PRIVATE: For client-side rehydration of server match.???
matchContext: object
},// 添加this.props.render方法
getDefaultProps: function getDefaultProps() {
return {
render: function render(props) {
return _react2.default.createElement(_RouterContext2.default,props);
}
};
},getInitialState: function getInitialState() {
// 路径改变时赋值location路径数据、routes激活的Route、params路径变量数据、components挂载的组件
// 通过componentWillMount方法中调用this.transitionManager.listen实现
return {
location: null,routes: null,params: null,components: null
};
},// 错误处理
handleError: function handleError(error) {
if (this.props.onError) {
this.props.onError.call(this,error);
} else {
throw error;
}
},// 创建对象,内含绑定路由事件、获取路由数据、跳转路由、判断路径是否激活相关方法
// location、params、routes属性访问当前state的数据
createRouterObject: function createRouterObject(state) {
var matchContext = this.props.matchContext;
if (matchContext) {
return matchContext.router;
}
// props.history为hashHistory、broswerHistroy、memoryHistory内含绑定路由事件、获取路由数据、跳转路由相关方法
var history = this.props.history;
// _RouterUtils.createRouterObject方法用于创建复合对象,含有histroy绑定路由事件、获取路由数据、跳转路由相关方法
// 以及setRouteLeaveHook、isActive方法
// 及location、params、routes属性(同Router组件当前state的相关属性等值)
return (0,_RouterUtils.createRouterObject)(history,this.transitionManager,state);
},// 创建transitionManager对象,用于监听、取消监听popstate、hashChange事件
createTransitionManager: function createTransitionManager() {
var matchContext = this.props.matchContext;
if (matchContext) {
return matchContext.transitionManager;
}
var history = this.props.history;// hashHistory、broswerHistroy、memoryHistory
var _props = this.props,routes = _props.routes,// 路由设置规则<route path="" component="">
children = _props.children;// 路由设置规则<route path="" component="">
!history.getCurrentLocation ? process.env.NODE_ENV !== 'production' ?
(0,_invariant2.default)(false,'You have provided a history object created with history v2.x or '
+ 'earlier. This version of React Router is only compatible with v3 '
+ 'history objects. Please upgrade to history v3.x.')
: (0,_invariant2.default)(false) : void 0;
return (0,_createTransitionManager3.default)(history,(0,_RouteUtils.createRoutes)(routes || children));
},// 创建transitionManager对象用于绑定事件,router对象用于操作路由、访问当前路由状况
// 绑定事件,哈希路径改变时更新router对象数据、更新组件、触发props.onUpdate回调
componentWillMount: function componentWillMount() {
var _this = this;
// 创建transitionManager对象,用于监听、取消监听popstate、hashChange事件
this.transitionManager = this.createTransitionManager();
// 创建对象,内含绑定路由事件、获取路由数据、跳转路由、判断路径是否激活相关方法
// location、params、routes属性访问当前state的数据
this.router = this.createRouterObject(this.state);
// 哈希路径变更时,触发histroy回调
// 通过回调触发Router组件的更新router对象数据、更新setState,以及props.onUpdate函数的执行
// 获取获取state信息为{routes,component}形式,执行回调
// 并触发Route组件配置的route.onChange|onEnter|onLeave方法执行
this._unlisten = this.transitionManager.listen(function (error,state) {
if (error) {
_this.handleError(error);
} else {
// 更新this.router中的location、params、routes数据,指向state即当前页面路径相关数据
(0,_RouterUtils.assignRouterState)(_this.router,state);
_this.setState(state,_this.props.onUpdate);
}
});
},// 提示不能重设props.histroy|routes|children
componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
process.env.NODE_ENV !== 'production' ?
(0,_routerWarning2.default)(nextProps.history === this.props.history,'You cannot change <Router history>; it will be ignored')
: void 0;
process.env.NODE_ENV !== 'production' ?
(0,_routerWarning2.default)(
(nextProps.routes || nextProps.children) === (this.props.routes || this.props.children),'You cannot change <Router routes>; it will be ignored'
) : void 0;
},// 解绑事件
componentWillUnmount: function componentWillUnmount() {
if (this._unlisten) this._unlisten();
},render: function render() {
var _state = this.state,location = _state.location,routes = _state.routes,params = _state.params,components = _state.components;
var _props2 = this.props,createElement = _props2.createElement,render = _props2.render,props = _objectWithoutProperties(_props2,['createElement','render']);
if (location == null) return null; // Async match
// Router.props部分属性和方法不拷贝给RouterContent渲染模块
Object.keys(Router.propTypes).forEach(function (propType) {
return delete props[propType];
});
// 调用props.render创建reactElement元素
return render(_extends({},props,{
router: this.router,location: location,routes: routes,params: params,components: components,createElement: createElement
}));
}
});
exports.default = Router;
module.exports = exports['default'];
2.createTransitionManager.js
Router.js模块中调用,用于创建this.transition对象绑点事件,Router.js模块中直接调用this.transition.listen方法监听hashChange、popstate事件,更新Router组件的state={location,components},引起RouterContent组件的props变更,进而实现RouterContent组件重绘,渲染当前路径下待挂载的components组件。
this.transition含有isActive、listenBeforeLeavingRoute、match、listen四个方法。
isActive(location,indexOnly) 该方法同时会挂载到Router组件的this.router.isActive方法上,因此使用withRouter(WrapComponent)封装的WrapComponent组件通过this.content.router.isActive可以访问该接口。该方法的意义是判断location路径数据同当前页面的路径数据是否匹配:indexOnly为真时,通过路径数据location解析到的pathname需要与当前页面的pathname严格相等,这在react-router内部判断IndexLink组件路径是否匹配当前页面路径时使用;indexOnly为否时,通过当前激活的routes判断location路径数据是否匹配当前页面路径背后由Route元素设定的路由规则。
listenBeforeLeavingRoute(route,hook)该方法同时会挂载到Router组件的this.router.setRouteLeaveHook方法上,因此使用withRouter(WrapComponent)封装的WrapComponent组件通过this.content.router.setRouteLeaveHook可以访问该接口。当页面离开route时,将执行hook函数,返回否值阻止页面跳转;字符串值将弹出警告窗口,这在react-route的依赖history模块中实现。
match(location,callback) 该方法只在react-route内部使用,即Router.js模块内通过this.transition.listen监听hashChange、popstate事件绑点函数时将调用,传入的首参location为待变更的路径数据,次参callback变更Router组件的state={location,components},并引起RouterContent组件重绘。传入首参location的实质性意义是通过待变更路径数据获取路径数据变量params、即将激活的routes元素(包括Route、IndexRoute、Redirect、IndexRedirect元素),由routes元素获取待挂载的组件components,最终获得待更待state={location,components}数据或者Redirect、IndexRedirect元素激活时的重定向地址redirectLocation,实现页面跳转或更新Router元素的state。
listen(listener)该方法只在react-route内部使用,即Router.js模块中调用,意义是监听hashChange、popstate事件,通过当前页面路径数据获取更迭后的state={location,components}或重定向路径数据redirectLocation,触发Router组件setState方法,重绘RouterContent组件。
'use strict';
exports.__esModule = true;
// 类似jquery.extend,将带靠背对象浅拷贝给目标对象
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source,key)) {
target[key] = source[key];
}
}
}
return target;
};
exports.default = createTransitionManager;
// 封装warning模块,目的是使含'deprecated'的警告文案只提示一次
var _routerWarning = require('./routerWarning');
var _routerWarning2 = _interoprequiredefault(_routerWarning);
// computeChangedRoutes(prevState,nextState),state.routes是Route组件的配置,数组形式
// 获取prevState待改变的route项leaveRoutes,nextState中保持与prevState相同的route项changeRoutes
// nextState中与prevState相同的route项changeRoutes,或者route.path改变,或者state.params改变
var _computeChangedRoutes2 = require('./computeChangedRoutes');
var _computeChangedRoutes3 = _interoprequiredefault(_computeChangedRoutes2);
// 通过runChangeHooks|runEnterHooks|runLeaveHooks间接执行route.onChange|onEnter|onLeave方法
var _TransitionUtils = require('./TransitionUtils');
// isActive(_ref,indexOnly,currentLocation,params)判断路径数据_ref同当前路径是否相同
// 参数_ref待校验的路径数据location
// indexOnly为真值时路径pathname需要完全匹配,否则pathname匹配激活的route设下的路由规则即可
// 参数currentLocation,params当前页面相关数据
var _isActive2 = require('./isActive');
var _isActive3 = _interoprequiredefault(_isActive2);
// getComponents(nextState,callback)获取激活路径下挂载的多个组件,并执行回调
var _getComponents = require('./getComponents');
var _getComponents2 = _interoprequiredefault(_getComponents);
// matchRoutes(routes,callback)获取当前路径下被激活的Route组件以及路径变量信息params,{routes,params}形式
// 并且将{routes,params}传入回调callback函数作为参数,routes中含有indexRoute元素的配置信息已获取待加载的组件
var _matchRoutes = require('./matchRoutes');
var _matchRoutes2 = _interoprequiredefault(_matchRoutes);
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// 判断object对象有否自有属性
function hasAnyProperties(object) {
for (var p in object) {
if (Object.prototype.hasOwnProperty.call(object,p)) return true;
}
return false;
}
// 创建transitionManager对象,用于监听、取消监听popstate、hashChange事件
// 参数history为hashHistory、broswerHistroy、memoryHistory中的一种
// 参数routes为Router元素下挂载的子元素this.props.children或this.props.routes
function createTransitionManager(history,routes) {
var state = {};
// isActive(location,indexOnly)判断location同当前路径是否相同
// indexOnly为真值时路径pathname需要完全匹配,否则pathname匹配激活的route设下的路由规则即可
function isActive(location,indexOnly) {
// 创建location数据对象
location = history.createLocation(location);
// isActive(_ref,params)判断路径数据_ref同当前路径是否相同
// 参数_ref待校验的路径数据location
// indexOnly为真值时路径pathname需要完全匹配,否则pathname匹配激活的route设下的路由规则即可
// 参数currentLocation,params当前页面相关数据
return (0,_isActive3.default)(location,state.location,state.routes,state.params);
}
var partialNextState = void 0;
// 参数location变更后的路径数据
// 获取激活的Route信息、待挂载的组件信息、路径变量信息(即state数据信息)
// state以{routes,component}形式传入callback中,并执行callback
// 期间触发Route组件配置的route.onChange|onEnter|onLeave方法执行
function match(location,callback) {
if (partialNextState && partialNextState.location === location) {
// Continue from where we left off.
finishMatch(partialNextState,callback);
} else {
// matchRoutes(routes,params}形式
// 并且将{routes,params}传入回调callback函数作为参数
(0,_matchRoutes2.default)(routes,function (error,nextState) {
if (error) {
callback(error);
} else if (nextState) {
finishMatch(_extends({},nextState,{ location: location }),callback);
} else {
callback();
}
});
}
}
// 参数nextState由match函数传入,{routes,location}形式
// 触发route.onChange|onEnter|onLeave方法执行
function finishMatch(nextState,callback) {
// computeChangedRoutes(prevState,nextState),state.routes是Route组件的配置,数组形式
// 获取prevState待改变的route项leaveRoutes,nextState中保持与prevState相同的route项changeRoutes
// nextState中与prevState相同的route项changeRoutes,或者route.path改变,或者state.params改变
var _computeChangedRoutes = (0,_computeChangedRoutes3.default)(state,nextState),leaveRoutes = _computeChangedRoutes.leaveRoutes,changeRoutes = _computeChangedRoutes.changeRoutes,enterRoutes = _computeChangedRoutes.enterRoutes;
// 间接执行Route及IndexRoute及Redirect组件上用户设置的onLeave方法
(0,_TransitionUtils.runLeaveHooks)(leaveRoutes,state);
// Tear down confirmation hooks for left routes ???
leaveRoutes.filter(function (route) {
return enterRoutes.indexOf(route) === -1;
}).forEach(removeListenBeforeHooksForRoute);
// 间接执行Route及IndexRoute及Redirect组件上用户设置的onChange方法
(0,_TransitionUtils.runChangeHooks)(changeRoutes,state,redirectInfo) {
if (error || redirectInfo) return handleErrorOrRedirect(error,redirectInfo);
// 间接执行Route及IndexRoute及Redirect组件上用户设置的onEnter方法
// Redirect元素被激活时,将触发该元素的onEnter方法,重定向页面路径
(0,_TransitionUtils.runEnterHooks)(enterRoutes,finishEnterHooks);
});
function finishEnterHooks(error,redirectInfo);
// getComponents(nextState,callback)获取激活路径下挂载的多个组件,并执行回调
(0,_getComponents2.default)(nextState,components) {
if (error) {
callback(error);
} else {
// callback执行参数state为{routes,location}形式
callback(null,null,state = _extends({},{ components: components }));
}
});
}
function handleErrorOrRedirect(error,redirectInfo) {
if (error) callback(error);else callback(null,redirectInfo);
}
}
var RouteGuid = 1;
// route有__id__属性,返回该属性;否则次参为真值时,以RouteGuid创建__id__属性后返回
function getRouteID(route) {
var create = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
return route.__id__ || create && (route.__id__ = RouteGuid++);
}
var RouteHooks = Object.create(null);
// 获取用户调用transitionManager.listenBeforeLeavingRoute(route,hook)添加的前置钩子执行函数
// transitionManager.listenBeforeLeavingRoute在Router模块中可通过this.transition调用
// 在使用withRouter(WrappedComponent)封装的WrappedComponent模块中可通过this.props.router.setRouteLeaveHook调用
function getRouteHooksForRoutes(routes) {
return routes.map(function (route) {
return RouteHooks[getRouteID(route)];
}).filter(function (hook) {
return hook;
});
}
// 执行用户调用transitionManager.listenBeforeLeavingRoute(route,hook)添加的前置钩子执行函数
// 返回值result交给封装了迭代器next、done方法的callback回调处理
function transitionHook(location,callback) {
// matchRoutes(routes,params}形式
// 并且将{routes,params}传入回调callback函数作为参数nextState
(0,nextState) {
if (nextState == null) {
callback();
return;
}
partialNextState = _extends({},{ location: location });
// 获取用户调用transitionManager.listenBeforeLeavingRoute(hook)添加的前置钩子执行函数
// _computeChangedRoutes3.default(prevState,nextState)计算确切的changeRoutes、leaveRoutes、enterRoutes
var hooks = getRouteHooksForRoutes((0,partialNextState).leaveRoutes);
var result = void 0;
for (var i = 0,len = hooks.length; result == null && i < len; ++i) {
result = hooks[i](location);
}
callback(result);
});
}
function beforeUnloadHook() {
// Synchronously check to see if any route hooks want
// to prevent the current window/tab from closing.
if (state.routes) {
var hooks = getRouteHooksForRoutes(state.routes);
var message = void 0;
for (var i = 0,len = hooks.length; typeof message !== 'string' && i < len; ++i) {
// Passing no args indicates to the user that this is a
// beforeunload hook. We don't know the next location.
message = hooks[i]();
}
return message;
}
}
var unlistenBefore = void 0,unlistenBeforeUnload = void 0;
function removeListenBeforeHooksForRoute(route) {
var routeID = getRouteID(route);
if (!routeID) {
return;
}
delete RouteHooks[routeID];
if (!hasAnyProperties(RouteHooks)) {
if (unlistenBefore) {
unlistenBefore();
unlistenBefore = null;
}
if (unlistenBeforeUnload) {
unlistenBeforeUnload();
unlistenBeforeUnload = null;
}
}
}
// 添加离开某route时执行的函数hook,通过history.listenBefore添加前置钩子实现
// 事件触发时通过computeChangedRoutes计算变更前后的state获取leaveRoutes,从RouteHooks得到添加的函数hook并执行
// hook返回否值阻止页面跳转;返回字符串将弹出警告窗口,该机制在history模块中实现
function listenBeforeLeavingRoute(route,hook) {
var thereWereNoRouteHooks = !hasAnyProperties(RouteHooks);
// 以RouteGuid创建route的__id__属性后返回
var routeID = getRouteID(route,true);
RouteHooks[routeID] = hook;
if (thereWereNoRouteHooks) {
// history.listenBefore添加前置钩子,路径变更触发时借由histroy模块中histroy.js文件下confirmTransitionTo函数执行
// confirmTransitionTo函数以updateLocation变更路径函数作为内置回调
// 因此实现了前置钩子执行完成后,更迭路径数据及变更页面路径
// 前置钩子以待变更路径数据location作为首参,封装迭代器方法next、done的函数作为次参,由confirmTransitionTo执行函数传入
// transitionHook函数将执行listenBeforeLeavingRoute添加的hook函数
// hook返回false时中止页面路径改变
unlistenBefore = history.listenBefore(transitionHook);
// history.listenBeforeUnload???
if (history.listenBeforeUnload) unlistenBeforeUnload = history.listenBeforeUnload(beforeUnloadHook);
}
return function () {
removeListenBeforeHooksForRoute(route);
};
}
// Router模块中内部使用,挂载指定回调,即更新Router组件及触发组件的props.onUpdate方法执行
function listen(listener) {
function historyListener(location) {
if (state.location === location) {
listener(null,state);
} else {
// 获取state信息为{routes,component}形式,执行回调
// 期间触发Route组件配置的route.onChange|onEnter|onLeave方法执行
match(location,redirectLocation,nextState) {
if (error) {
listener(error);
} else if (redirectLocation) {
history.replace(redirectLocation);
} else if (nextState) {
listener(null,nextState);
} else {
process.env.NODE_ENV !== 'production' ?
(0,_routerWarning2.default)(false,'Location "%s" did not match any routes',location.pathname + location.search + location.hash)
: void 0;
}
});
}
}
// 监听hashChange或popstate事件,绑定回调函数historyListener
var unsubscribe = history.listen(historyListener);
// 初始化更新Router组件及触发组件的props.onUpdate方法执行
if (state.location) {
listener(null,state);
} else {
historyListener(history.getCurrentLocation());
}
// 返回解绑函数
return unsubscribe;
}
return {
// isActive(location,indexOnly)判断location同当前路径是否相同
// indexOnly为真值时路径pathname需要完全匹配,否则pathname匹配激活的route设下的路由规则即可
// IndexRouter、IndexRedirect组件使用,判断是否根路径
isActive: isActive,// 内部listen方法调动,match(location,callback)参数location变更后的路径数据
// 获取激活的Route信息、待挂载的组件信息、路径变量信息(即state数据信息)
// state以{routes,component}形式传入callback中,并执行callback
// 期间触发Route组件配置的route.onChange|onEnter|onLeave方法执行
match: match,// listenBeforeLeavingRoute(route,hook)添加离开某route时执行的函数hook
// 通过history.listenBefore添加前置钩子实现
// 事件触发时通过computeChangedRoutes计算变更前后的state获取leaveRoutes,从RouteHooks得到添加的函数hook并执行
// hook返回否值阻止页面跳转;返回字符串将弹出警告窗口,该机制在history模块中实现
listenBeforeLeavingRoute: listenBeforeLeavingRoute,// 监听hashChange或popstate事件,回调中更新Router组件及触发组件的props.onUpdate方法执行
listen: listen
};
}
module.exports = exports['default'];
3.RouterUtils.js
Router.js模块中调用,用于创建this.router对象。
this.router通过拷贝形式获得histroy的方法,因此能操作变更路径、绑点函数等;同时this.router获得this.transition的isActive(location,indexOnly)方法,setRouteLeaveHook(route,hook)方法也引用this.transition的listenBeforeLeavingRoute方法;this.router还获得location、params、routes属性,用于访问当前页面下路径数据、路径变量和激活的route元素。
Router组件的this.router通过getChildContext传入子组件如Link、IndexLink、及使用withRouter(WrappedComponent)形式挂载的WrappedComponent元素中。这些元素的this.context.router即指向Router组件的this.router。Link、IndexLink组件调用this.context.router.push | createHref实现点击跳转功能;WrappedComponent组件通过this.context.router向props注入{router,routes},元素由对应的路径数据、路径变量、激活的route元素完成实例化。
Router.js模块提供createRouterObject、assignRouterState两个方法。createRouterObject用于创建this.router对象,assignRouterState用于路径变更,引起Router组件setState方法执行前,更新this.router的location、params、routes属性指向待变更路径数据的对应属性。
location 当前页面的路径数据
params 当前页面的路径变量
routes 当前页面激活的route元素,包含Route、IndexRoute、Redirect、IndexRedirect元素
isActive(location,indexOnly) 判断location路径数据是否符合激活route元素设定的路由规则;indexOnly为真值时,pathname须严格相等,对应IndexRoute、IndexRedirect情形。
setRouteLeaveHook(route,hook) 页面离开route元素时,执行hook函数。hook返回值为字符串,弹框提示;为否值时,阻止页面跳转。
部分方法以及源自history模块:
getCurrentLocation 获取当前页面的location路径数据。
listenBefore( function hook(location,callback){} ) 添加前置钩子hook。hook函数的参数location、callback都由history模块的内部机制传入。其中,首参location为待跳转的页面路径数据;次参callback为封装了迭代器next、done方法的函数,用于控制流程。hook返回否值,阻止页面跳转;返回字符串,弹框提示。
listen( function listener(location){} ) 添加后置钩子listener,页面跳转时执行的回调函数。
push(nextPath)、replace(nextPath)@R_202_404@面,将触发前置钩子和后置钩子的执行。
go、goBack、goForward 通过window.history.go@R_202_404@面。
createPath(location)、createHref(location) 生成路径。
"use strict";
exports.__esModule = true;
// 类似jquery.extend,将带靠背对象浅拷贝给目标对象
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source,key)) {
target[key] = source[key];
}
}
}
return target;
};
// createRouterObject(history,state)方法
// 创建复合对象,含有histroy绑定路由事件、获取路由数据、跳转路由相关方法
// 以及setRouteLeaveHook、isActive方法
// 及location、params、routes属性(同Router组件当前state的相关属性等值)
exports.createRouterObject = createRouterObject;
// assignRouterState(router,_ref)拷贝_ref的location、params、routes属性给router
// 次参_ref通常是Router组件当前的state,用于更新createRouterObject创建的复合对象的state相关数据
exports.assignRouterState = assignRouterState;
// histroy为hashHistory、broswerHistroy、memoryHistory内含绑定路由事件、获取路由数据、跳转路由相关方法
// 复合histroy的属性和方法,添加setRouteLeaveHook、isActive方法,location、params、routes属性后返回
function createRouterObject(history,state) {
var router = _extends({},history,{
setRouteLeaveHook: transitionManager.listenBeforeLeavingRoute,isActive: transitionManager.isActive
});
return assignRouterState(router,state);
}
// 拷贝次参对象的location、params、routes属性给首参对象
// Router.js模块中调用,用于路径变更时更新this.router.location | params | routes信息
function assignRouterState(router,_ref) {
var location = _ref.location,params = _ref.params,routes = _ref.routes;
router.location = location;
router.params = params;
router.routes = routes;
return router;
}
4.RouterContent.js
RouterContent.js实现Route、IndexRoute元素所挂载组件的渲染,props={router,createElement}借由Router.js监听hashChange、popstate事件触发setState方法执行、完成更迭。
当通过<Route path="message/:id" component={Component}/>挂载Component元素时,该Component元素的props形式为{location,router,route,routeParams},其中location,routes为当前页面对应的路径数据、路径变量、激活的route元素(如Route、IndexRoute、Redirect、IndexRedirect元素);router为Router元素的this.router,用于操作页面跳转、监听路径跳转、获取当前的路径数据等;route为该Component元素相应的Route或IndexRoute元素;routeParams为与该Component元素对应的Route,其下设的路径变量数据,如{id:1}形式。
特别的,当通过<Route path="/" component={App}><Routepath="message/:id"components={key:Component}/> </Route>挂载Component元素时,父元素App将获得props.key=Component,用于render方法渲染绘制。
'use strict';
exports.__esModule = true;
// 类似jquery.extend,将带靠背对象浅拷贝给目标对象
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source,key)) {
target[key] = source[key];
}
}
}
return target;
};
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
// node模块;invariant(condition,f替换报错模板字符串format,抛出错误
// _invariant2.default引用_invariant,即node模块invariant
var _invariant = require('invariant');
var _invariant2 = _interoprequiredefault(_invariant);
var _react = require('react');
var _react2 = _interoprequiredefault(_react);
// 通过route获取路径变量的键,再注入params相应键的值,意为当前route路径的变量
var _getRouteParams = require('./getRouteParams');
var _getRouteParams2 = _interoprequiredefault(_getRouteParams);
var _ContextUtils = require('./ContextUtils');
// 提供一系列方法,目的是将Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
// 其中isReactChildren(element)方法用于校验单个或一组元素是否reactElement
var _RouteUtils = require('./RouteUtils');
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _React$PropTypes = _react2.default.PropTypes,array = _React$PropTypes.array,object = _React$PropTypes.object;
// 负责渲染Router下子Route挂载的组件,由Router的state数据获取到激活的Route组件及其components待挂载的组件
// 通过底层的Route组件向上遍历父Route,创建待挂载渲染的props.component|components组件元素reactElement实现
var RouterContext = _react2.default.createClass({
displayName: 'RouterContext',// 向下游组件通过context传递this.context[contextName].subscribe添加绑定事件方法
// this.context[contextName].eventIndex访问跳转路径发生次数
// 意义是下游组件如Link通过绑定函数监听路径变化事件发生,并重绘Link组件
mixins: [(0,_ContextUtils.ContextProvider)('router')],propTypes: {
router: object.isrequired,// 通过Router组件的this.router传入,操作路由、获取数据方法或属性
location: object.isrequired,// 路径信息数据
routes: array.isrequired,// 当前路径下被激活的Route组件
params: object.isrequired,// 路径变量
components: array.isrequired,// 待挂载渲染的组件
createElement: func.isrequired// 通过Router组件的props传入,用户未设置,默认为react.createElement
},// props.createElement默认赋值为react.createElement
getDefaultProps: function getDefaultProps() {
return {
createElement: _react2.default.createElement
};
},childContextTypes: {
router: object.isrequired
},// 向子Route传递router,即Router组件的this.router,可操作路由、获取数据方法或属性
getChildContext: function getChildContext() {
return {
router: this.props.router
};
},createElement: function createElement(component,props) {
return component == null ? null : this.props.createElement(component,props);
},render: function render() {
var _this = this;
var _props = this.props,location = _props.location,params = _props.params,components = _props.components,router = _props.router;
var element = null;
if (components) {
// 由底层向上创建待挂载渲染的route.props.component|components组件元素
element = components.reduceRight(function (element,index) {
if (components == null) return element; // Don't create new children; use the grandchildren.
var route = routes[index];
var routeParams = (0,_getRouteParams2.default)(route,params);
var props = {
location: location,route: route,router: router,routeParams: routeParams,routes: routes
};
// 校验element是否react元素(可以是数组形式),嵌套子Route待挂载的组件,作为父组件的props.children
if ((0,_RouteUtils.isReactChildren)(element)) {
props.children = element;
// 子Route使用components={key:component}形式定义待挂载的组件,父组件通过this.props[key]获取元素
} else if (element) {
for (var prop in element) {
if (Object.prototype.hasOwnProperty.call(element,prop)) props[prop] = element[prop];
}
}
// Route使用components={key:component}形式定义待挂载的组件,创建react元素
if ((typeof components === 'undefined' ? 'undefined' : _typeof(components)) === 'object') {
var elements = {};
for (var key in components) {
if (Object.prototype.hasOwnProperty.call(components,key)) {
elements[key] = _this.createElement(components[key],_extends({
key: key },props));
}
}
return elements;
}
return _this.createElement(components,props);
},element);
}
!(element === null || element === false || _react2.default.isValidElement(element)) ?
process.env.NODE_ENV !== 'production' ?
(0,'The root route must render a single element')
: (0,_invariant2.default)(false) : void 0;
return element;
}
});
exports.default = RouterContext;
module.exports = exports['default'];
5.Route.js、IndexRoute.js
Route用于约定某路由下待加载的组件,JSX书写方式为<Route path="pathname" component={Component}/>。特别的,IndexRoute用于约定父Route默认加载的组件,JSX书写方式为<Route path="pathname1" component={Component1}><IndexRoutecomponent={Component2}/><Route path="pathname2" component={Component3}/></Route>,即pathname1路径下将渲染Component2,pathname1/pathname2路径下将渲染Component3。
Route、IndexRoute组件不负责渲染,用于向Router组件提供props.routes(形式为[{path:"",component:Component}])。path用于定义路由匹配规则,component | components | getComponent | getComponents定义待加载的组件。Router.js模块调用this.transition.listen方法监听hashChange、popstate事件时,绑点函数将通过matchRoutes.js模块的matchRoutes方法,由当前页面路径逐个匹配Router组件props.routes下的路由规则,获得被激活的routes元素(包含Route、IndexRoute、Redirect、IndexRedirect元素),从而得到当前页面下待加载的组件。
Route、IndexRoute组件提供createRouteFromReactElement静态方法,实现功能为:当Route、IndexRoute、Redirect、IndexRedirect元素使用JSX语法添加到Router元素下时,Router.js模块将调用RouteUtils.js模块的creatRoutes方法将JSX语法书写的Route、IndexRoute、Redirect、IndexRedirect元素转化为props.routes形式,供matchRoutes.js模块使用。
Route、IndexRoute元素可设置的事件:
props.onLeave(prevState) 离开时调用函数
props.onChange(state,replace) 路径改变时调用函数,replace即history.replace(location)方法,用 于页面跳转
props.onEnter(nextState,replace) route激活时调用函数
Route.js源码
'use strict';
exports.__esModule = true;
var _react = require('react');
var _react2 = _interoprequiredefault(_react);
// node模块;invariant(condition,f替换报错模板字符串format,抛出错误
// _invariant2.default引用_invariant,即node模块invariant
var _invariant = require('invariant');
var _invariant2 = _interoprequiredefault(_invariant);
// 提供一系列方法,目的是将Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
var _RouteUtils = require('./RouteUtils');
var _InternalPropTypes = require('./InternalPropTypes');
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _React$PropTypes = _react2.default.PropTypes,string = _React$PropTypes.string,func = _React$PropTypes.func;
// var props={
// path: string,// 对应的页面路径
// component,// 待渲染的组件
// components,// 键值对形式设置加载的子组件,其中键作为props.key
// getComponent,// 函数形式获取加载的子组件
// getComponents,// onLeave(prevState),// 离开时调用函数
// onChange(state,replace),// 路径改变时调用函数
// onEnter(nextState,// route激活时调用函数
// }
// <Route {...props}>
// 用于定义某路径下待加载的components组件,以及离开、进入、跳转时执行的函数,渲染components通过RouterContent模块实现
// 由matchRoutes模块获取当前路径下激活的route及indexRoute及Redirect元素
// 再通过createTransitionManager模块根据激活的route获取待加载的组件
// 最后Router绑定hashchange事件setState时重绘组件
var Route = _react2.default.createClass({
displayName: 'Route',// 添加静态属性或方法
// createRouteFromReactElement静态方法在RouteUtils模块中使用
// 作用是将Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
// 子Route组件作为父Route组件的props.childRoutes属性
statics: {
createRouteFromReactElement: _RouteUtils.createRouteFromReactElement
},propTypes: {
path: string,// 对应的页面路径
component: _InternalPropTypes.component,// 待渲染的组件
components: _InternalPropTypes.components,// 键值对形式设置加载的子组件,其中键作为props.key
getComponent: func,// 函数形式获取加载的子组件
getComponents: func
//onLeave(prevState) 离开时调用函数
//onChange(state,replace) 路径改变时调用函数
//onEnter(nextState,replace) route激活时调用函数
},// 不负责渲染
render: function render() {
!false ? process.env.NODE_ENV !== 'production' ?
(0,'<Route> elements are for router configuration only and should not be rendered')
: (0,_invariant2.default)(false) : void 0;
}
});
exports.default = Route;
module.exports = exports['default'];
IndexRoute.js源码
'use strict';
exports.__esModule = true;
var _react = require('react');
var _react2 = _interoprequiredefault(_react);
// 封装warning模块,目的是使含'deprecated'的警告文案只提示一次
var _routerWarning = require('./routerWarning');
var _routerWarning2 = _interoprequiredefault(_routerWarning);
// node模块;invariant(condition,f替换报错模板字符串format,抛出错误
// _invariant2.default引用_invariant,即node模块invariant
var _invariant = require('invariant');
var _invariant2 = _interoprequiredefault(_invariant);
// 提供一系列方法,目的是将Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
var _RouteUtils = require('./RouteUtils');
var _InternalPropTypes = require('./InternalPropTypes');
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var func = _react2.default.PropTypes.func;
// <Router>
// <Route path="/" component={App}>
// <IndexRoute component={Home}/>
// <Route path="accounts" component={Accounts}/>
// <Route path="statements" component={Statements}/>
// </Route>
// </Router>
// JSX书写IndexRoute,意义是访问"/"路径时加载Home模块,作为根路径下加载的子模块
// IndexRoute必然作为Route的子组件,JSX配置转化为类props配置时,父route获得indexRoute属性指向子IndexRoute
// 由matchRoutes模块获取当前路径下激活的route及indexRoute及Redirect元素
// 再通过createTransitionManager模块根据激活的route获取待加载的组件
// 最后Router绑定hashchange事件setState时重绘组件
var IndexRoute = _react2.default.createClass({
displayName: 'IndexRoute',// createRouteFromReactElement静态方法在RouteUtils模块中使用
// 作用是将Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
statics: {
createRouteFromReactElement: function createRouteFromReactElement(element,parentRoute) {
if (parentRoute) {
// IndexRoute组件作为父Route组件的props.childRoutes属性的同时,也作为其indexRoute属性
parentRoute.indexRoute = (0,_RouteUtils.createRouteFromReactElement)(element);
} else {
process.env.NODE_ENV !== 'production' ?
(0,'An <IndexRoute> does not make sense at the root of your route config')
: void 0;
}
}
},propTypes: {
path: _InternalPropTypes.falsy,component: _InternalPropTypes.component,components: _InternalPropTypes.components,getComponent: func,getComponents: func
},render: function render() {
!false ? process.env.NODE_ENV !== 'production' ?
(0,'<IndexRoute> elements are for router configuration only and should not be rendered')
: (0,_invariant2.default)(false) : void 0;
}
});
exports.default = IndexRoute;
module.exports = exports['default'];
6.Redirect.js、IndexRedirect.js
Redirect.js模块实现,当用户访问指定路径时,将页面跳转到另一指定路径。
IndexRedirect.js模块实现,当用户访问IndexRedirect元素的父Route元素的路径时,将页面跳转到指定路径。
实现机制是,Redirect、IndexRedirect组件也设置createRouteFromReactElement静态方法获取route,当Router组件调用RouteUtils.createRoutes方法时也将Redirect、IndexRedirect转化为props.routes内数据。因此当hashChange、popState事件触发时,也将获得激活的Redirect、IndexRedirect元素,并调用route.onEnter跳转到props.to路径下。
Redirect.js源码
'use strict';
exports.__esModule = true;
var _react = require('react');
var _react2 = _interoprequiredefault(_react);
// node模块;invariant(condition,f替换报错模板字符串format,抛出错误
// _invariant2.default引用_invariant,即node模块invariant
var _invariant = require('invariant');
var _invariant2 = _interoprequiredefault(_invariant);
// 提供一系列方法,目的是将Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
var _RouteUtils = require('./RouteUtils');
// 通过histroy设定的路径格式获取真实路径的正则匹配规则,或获取真实路径的路径变量,或通过路径变量和路径格式获取真实路径
var _PatternUtils = require('./PatternUtils');
var _InternalPropTypes = require('./InternalPropTypes');
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _React$PropTypes = _react2.default.PropTypes,object = _React$PropTypes.object;
// <Route path="inBox" component={InBox}>
// { 从 /inBox/messages/:id 跳转到 /messages/:id }
// <Redirect from="messages/:id" to="/messages/:id" />
// </Route>
// 重定向页面路径
var Redirect = _react2.default.createClass({
displayName: 'Redirect',statics: {
// matchRoutes模块通过调用RouteUtils模块中间接执行,用于判断Redirect元素是否被激活
createRouteFromReactElement: function createRouteFromReactElement(element) {
// 获取Redirect元素的props配置
// 同时在matchRoutes模块中根据当前路径获取的激活routes,将根据由Redirect元素得到的route.path
// 判断是否需要将Redirect元素的props配置,也加入激活的routes中
var route = (0,_RouteUtils.createRouteFromReactElement)(element);
if (route.from) route.path = route.from;
// Redirect元素激活,creatTransitionManager模块调用Redirect元素的props.onEnter,页面重定向
route.onEnter = function (nextState,replace) {
var location = nextState.location,params = nextState.params;
var pathname = void 0;
if (route.to.charAt(0) === '/') {
pathname = (0,_PatternUtils.formatPattern)(route.to,params);
} else if (!route.to) {
pathname = location.pathname;
} else {
var routeIndex = nextState.routes.indexOf(route);
var parentPattern = Redirect.getRoutePattern(nextState.routes,routeIndex - 1);
var pattern = parentPattern.replace(/\/*$/,'/') + route.to;
pathname = (0,_PatternUtils.formatPattern)(pattern,params);
}
replace({
pathname: pathname,query: route.query || location.query,state: route.state || location.state
});
};
// matchRoutes模块中判断Redirect是否在激活状态
// 若激活,通过creatTransitionManager模块调用Redirect元素的props.onEnter,页面重定向
return route;
},getRoutePattern: function getRoutePattern(routes,routeIndex) {
var parentPattern = '';
for (var i = routeIndex; i >= 0; i--) {
var route = routes[i];
var pattern = route.path || '';
parentPattern = pattern.replace(/\/*$/,'/') + parentPattern;
if (pattern.indexOf('/') === 0) break;
}
return '/' + parentPattern;
}
},from: string,// Alias for path
to: string.isrequired,query: object,state: object,onEnter: _InternalPropTypes.falsy,children: _InternalPropTypes.falsy
},'<Redirect> elements are for router configuration only and should not be rendered') :
(0,_invariant2.default)(false) : void 0;
}
});
exports.default = Redirect;
module.exports = exports['default'];
IndexRedirect.js源码
'use strict';
exports.__esModule = true;
var _react = require('react');
var _react2 = _interoprequiredefault(_react);
// 封装warning模块,目的是使含'deprecated'的警告文案只提示一次
var _routerWarning = require('./routerWarning');
var _routerWarning2 = _interoprequiredefault(_routerWarning);
// node模块;invariant(condition,f替换报错模板字符串format,抛出错误
// _invariant2.default引用_invariant,即node模块invariant
var _invariant = require('invariant');
var _invariant2 = _interoprequiredefault(_invariant);
var _Redirect = require('./Redirect');
var _Redirect2 = _interoprequiredefault(_Redirect);
var _InternalPropTypes = require('./InternalPropTypes');
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _React$PropTypes = _react2.default.PropTypes,object = _React$PropTypes.object;
// <Route path="/" component={App}>
// <IndexRedirect to="/welcome" />
// <Route path="welcome" component={Welcome} />
// <Route path="about" component={About} />
// </Route>
// 根路径下重定向页面路径
var IndexRedirect = _react2.default.createClass({
displayName: 'IndexRedirect',statics: {
createRouteFromReactElement: function createRouteFromReactElement(element,parentRoute) {
// 调用Redirect.createRouteFromReactElement获取IndexRedirect元素的props配置
// 同时,父元素route的indexRoute添加该props配置,实现也就跟IndexRoute组件相同
if (parentRoute) {
parentRoute.indexRoute = _Redirect2.default.createRouteFromReactElement(element);
} else {
process.env.NODE_ENV !== 'production' ?
(0,'An <IndexRedirect> does not make sense at the root of your route config')
: void 0;
}
}
},propTypes: {
to: string.isrequired,'<IndexRedirect> elements are for router configuration only and should not be rendered')
: (0,_invariant2.default)(false) : void 0;
}
});
exports.default = IndexRedirect;
module.exports = exports['default'];
7.Link.js、IndexLink.js
Link.js、IndexLink.js实现点击跳转功能,实现机制是通过this.context.router获得Router元素的this.router,在监听点击事件的绑定函数内部调用this.context.router.push方法跳转链接。
特别的,IndexLink组件设置样式时将向this.context.router.isActive方法传入indexOnly=true参数,待跳转的路径pathname须与当前页面路径的pathname完全匹配,IndexLink元素添加activeClassName、activeStyle样式。而Link元素的机制是,通过当前页面路径获取路由规则state.location、state.routes、state.params,只要待跳转的路径pathname匹配该路由规则,当即添加activeClassName、activeStyle样式。
hashChange、popstate事件触发、Router更新state时,Link、IndexLink元素将通过ContextUtils.js机制实现WrappedComponent元素的重新渲染
Link.js源码:
'use strict';
exports.__esModule = true;
// 类似jquery.extend,将带靠背对象浅拷贝给目标对象
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source,key)) {
target[key] = source[key];
}
}
}
return target;
};
var _react = require('react');
var _react2 = _interoprequiredefault(_react);
// node模块;invariant(condition,f替换报错模板字符串format,抛出错误
// _invariant2.default引用_invariant,即node模块invariant
var _invariant = require('invariant');
var _invariant2 = _interoprequiredefault(_invariant);
var _PropTypes = require('./PropTypes');
// RouterContext模块向下游组件通过context传递subscribe方法添加绑定事件方法,eventIndex获取跳转路径发生次数
// Link组件通过绑定函数监听路径变化事件发生,并调用setState重绘Link组件
var _ContextUtils = require('./ContextUtils');
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// obj的属性或方法在keys中也有,不予拷贝
function _objectWithoutProperties(obj,bool = _React$PropTypes.bool,object = _React$PropTypes.object,oneOfType = _React$PropTypes.oneOfType;
function isLeftClickEvent(event) {
return event.button === 0;
}
function isModifiedEvent(event) {
return !!(event.MetaKey || event.altKey || event.ctrlKey || event.shiftKey);
}
function isEmptyObject(object) {
for (var p in object) {
if (Object.prototype.hasOwnProperty.call(object,p)) return false;
}return true;
}
function resolveToLocation(to,router) {
return typeof to === 'function' ? to(router.location) : to;
}
// var props={
// to,// 待跳转的路径数据,string如`/posts/${post.id}`,object,function(router.location)以当前页面路径作为参数
// query:object,// hash: string,// state: object,// activeStyle,// props.to指向的路径为页面当前路径时,添加的样式
// activeClassName,// props.to指向的路径为页面当前路径时,添加的样式
// onlyActiveOnIndex,// activeClassName添加时待@R_202_404@面是否需要严格匹配路径
// onClick: func,// 点击事件触发时执行函数
// target: string// "_blank"链接打开的方式
// }
// <Route path="/posts/:postID" component={Post} >
// <Link to={`/posts/${post.id}`} />
// <Link ... query={{ show: true }} state={{ the: 'state' }} />
// </Route>
// 提供页面路径跳转功能,通过router.push方法实现,也即能触发Router下组件重绘
var Link = _react2.default.createClass({
displayName: 'Link',// Router下游Link监听路径变化事件发生,调用setState方法重绘组件
mixins: [(0,_ContextUtils.ContextSubscriber)('router')],contextTypes: {
router: _PropTypes.routerShape
},propTypes: {
to: oneOfType([string,object,func]),hash: string,activeStyle: object,activeClassName: string,onlyActiveOnIndex: bool.isrequired,onClick: func,target: string
},getDefaultProps: function getDefaultProps() {
return {
onlyActiveOnIndex: false,style: {}
};
},// 点击时通过router.push@R_202_404@面,router.push也即histroy.push方法
handleClick: function handleClick(event) {
if (this.props.onClick) this.props.onClick(event);
if (event.defaultPrevented) return;
var router = this.context.router;
!router ? process.env.NODE_ENV !== 'production' ?
(0,'<Link>s rendered outside of a router context cannot navigate.') :
0,_invariant2.default)(false) : void 0;
if (isModifiedEvent(event) || !isLeftClickEvent(event)) return;
if (this.props.target) return;
event.preventDefault();
router.push(resolveToLocation(this.props.to,router));
},render: function render() {
var _props = this.props,to = _props.to,activeClassName = _props.activeClassName,activeStyle = _props.activeStyle,onlyActiveOnIndex = _props.onlyActiveOnIndex,props = _objectWithoutProperties(_props,['to','activeClassName','activeStyle','onlyActiveOnIndex']);
var router = this.context.router;
if (router) {
if (to == null) {
return _react2.default.createElement('a',props);
}
var toLocation = resolveToLocation(to,router);
props.href = router.createHref(toLocation);
if (activeClassName || activeStyle != null && !isEmptyObject(activeStyle)) {
if (router.isActive(toLocation,onlyActiveOnIndex)) {
if (activeClassName) {
if (props.className) {
props.className += ' ' + activeClassName;
} else {
props.className = activeClassName;
}
}
if (activeStyle) props.style = _extends({},props.style,activeStyle);
}
}
}
return _react2.default.createElement('a',_extends({},{ onClick: this.handleClick }));
}
});
exports.default = Link;
module.exports = exports['default'];
IndexLink源码
'use strict';
exports.__esModule = true;
// 类似jquery.extend,将带靠背对象浅拷贝给目标对象
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source,key)) {
target[key] = source[key];
}
}
}
return target;
};
var _react = require('react');
var _react2 = _interoprequiredefault(_react);
var _Link = require('./Link');
var _Link2 = _interoprequiredefault(_Link);
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// <IndexLink to="/" activeClassName="active">
// Home
// </IndexLink>
// 跳转到根路径
var IndexLink = _react2.default.createClass({
displayName: 'IndexLink',render: function render() {
return _react2.default.createElement(_Link2.default,this.props,{ onlyActiveOnIndex: true }));
}
});
exports.default = IndexLink;
module.exports = exports['default'];
8.withRouter.js
withRouter.js模块用于将react组件WrappedComponent封装为高阶组件,并向WrappedComponent元素的props注入router操控路由、params当前页面的路径变量、location当前页面的路径数据、routes当前页面激活的route元素。同Link组件,hashChange、popstate事件触发、Router更新state时,将通过ContextUtils.js机制实现WrappedComponent元素的重新渲染。
'use strict';
exports.__esModule = true;
// 类似jquery.extend,将带靠背对象浅拷贝给目标对象
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source,key)) {
target[key] = source[key];
}
}
}
return target;
};
exports.default = withRouter;
// node模块;invariant(condition,f替换报错模板字符串format,抛出错误
// _invariant2.default引用_invariant,即node模块invariant
var _invariant = require('invariant');
var _invariant2 = _interoprequiredefault(_invariant);
var _react = require('react');
var _react2 = _interoprequiredefault(_react);
var _hoistNonReactStatics = require('hoist-non-react-statics');
var _hoistNonReactStatics2 = _interoprequiredefault(_hoistNonReactStatics);
var _ContextUtils = require('./ContextUtils');
var _PropTypes = require('./PropTypes');
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
// 与Link组件的机制相同,context获得subscribe方法添加绑定事件方法,eventIndex属性获取跳转路径发生次数
// 通过subscribe方法挂载withRouter的setState重绘方法,当RouterContent组件的componentDidUpdate方法触发时执行
// 同时props.router属性获得Router创建router,可用于操纵路由、获取路径数据
// 使用同普通组件一样,作为route的props.component属性
function withRouter(WrappedComponent,options) {
var withRef = options && options.withRef;
var WithRouter = _react2.default.createClass({
displayName: 'WithRouter',mixins: [(0,contextTypes: { router: _PropTypes.routerShape },propTypes: { router: _PropTypes.routerShape },getWrappedInstance: function getWrappedInstance() {
!withRef ? process.env.NODE_ENV !== 'production' ? (0,'To access the wrapped instance,you need to specify ' + '`{ withRef: true }` as the second argument of the withRouter() call.') : (0,_invariant2.default)(false) : void 0;
return this.wrappedInstance;
},render: function render() {
var _this = this;
var router = this.props.router || this.context.router;
var params = router.params,location = router.location,routes = router.routes;
var props = _extends({},{ router: router,routes: routes });
if (withRef) {
props.ref = function (c) {
_this.wrappedInstance = c;
};
}
return _react2.default.createElement(WrappedComponent,props);
}
});
WithRouter.displayName = 'withRouter(' + getDisplayName(WrappedComponent) + ')';
WithRouter.WrappedComponent = WrappedComponent;
return (0,_hoistNonReactStatics2.default)(WithRouter,WrappedComponent);
}
module.exports = exports['default'];
9.browserHistory.js、hashHistory.js、createMemoryHistory.js
browserHistory.js模块由histroy模块创建的browserHistory对象,用于@R_202_404@面,监听hashChange、popstate事件,获取当前页面路径数据等。
hashHistory.js模块由histroy模块创建的hashHistory对象,用于@R_202_404@面,监听hashChange、popstate事件,获取当前页面路径数据等。
createMemoryHistory.js模块调用histroy模块的createMemoryHistory,用于创建memoryHistory对象,实现@R_202_404@面,监听hashChange、popstate事件,获取当前页面路径数据等。
各history对象作为Router元素的props.history,使Router实现监听hashChange、popstate事件并更新state、重绘组件,以及this.router获得@R_202_404@面、添加前置钩子等方法。
browserHistory.js源码
'use strict';
exports.__esModule = true;
// createBrowserHistory(options),构建browserHistory对象,内含变更页面路径到更新location数据到执行绑点函数的一般方法
// options.forceRefresh 强制使用window.location.href(path)或window.location.replace(path)更新页面路径
// 返回值
// {
// getUserConfirmation,// getUserConfirmation(message,callback) 在callback回调中定制执行window.confirm(message)
// forceRefresh,// 强制使用window.location.href(path)或window.location.replace(path)更新页面路径
// getCurrentLocation,// 获取当前location数据{pathname,search,hash,action,key}
// pushLocation,// 路径重新赋值,并使用window.sessionStorage存储{[key]:state}形式的location路径数据
// replaceLocation,// 路径重新赋值,并使用window.sessionStorage存储{[key]:state}形式的location路径数据
// listenBefore,// 将transitionTo方法绑定到popState或hashChange事件上,添加前置钩子;事件触发时,执行钩子,及变更浏览器location及window.sessionStorage存储
// listen,// 将transitionTo方法绑定到popState或hashChange事件上,添加后置钩子;事件触发时,执行钩子,及变更浏览器location及window.sessionStorage存储
// transitionTo,// 变更页面路径、更新window.sessionStorage、执行钩子函数,更新allKeys,可作为页面路径变更时的回调
// push,// transitionTo方法变更页面路径、更新window.sessionStorage、执行钩子函数,allKeys添加一条路径记录,可作为页面路径变更时的回调
// replace,// transitionTo方法变更页面路径、更新window.sessionStorage、执行钩子函数,allKeys替换一条路径记录,可作为页面路径变更时的回调
// go,// window.history.go@R_202_404@面
// goBack,// window.history.go@R_202_404@面
// goForward,// window.history.go@R_202_404@面
// createKey,// 随机生成一个key,作为查询字符串中的值,以及window.sessionStorage中的键
// createPath,// 生成路径
// createHref,// 生成路径
// createLocation,// 生成location数据
// }
var _createBrowserHistory = require('history/lib/createBrowserHistory');
var _createBrowserHistory2 = _interoprequiredefault(_createBrowserHistory);
// 装饰createHistory函数后,并调用createHistory创建history对象
var _createRouterHistory = require('./createRouterHistory');
var _createRouterHistory2 = _interoprequiredefault(_createRouterHistory);
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// 创建history对象
// {
// getUserConfirmation,// 默认值false,即不使用window.location.href(path)或window.location.replace(path)更新页面路径
// getCurrentLocation,// 生成location数据
// }
exports.default = (0,_createRouterHistory2.default)(_createBrowserHistory2.default);
module.exports = exports['default'];
hashHistory.js、createRouterHistory.js源码
'use strict';
exports.__esModule = true;
// createHashHistory(options),创建hashHistory对象,内含变更页面路径到更新location数据到执行绑点函数的一般方法
// options.queryKey 不能设置为false,哈希路径中查询字符串的键,值为window.sessionStorage存储state的键
// options.hashType 须是hashbang、noslash、slash中的一个,默认slash,规定哈希路径hashPath类型
// hashbang以"!"起始,noslash直接跟路径,slash以"/"起始
// 返回值
// {
// getUserConfirmation,callback) 在callback回调中定制执行window.confirm(message)
// queryKey,// 哈希路径中查询字符串的键,值为window.sessionStorage存储state的键
// hashType,// 规定哈希路径hashPath书写形式
// getCurrentLocation,// 哈希路径重新赋值,并使用window.sessionStorage存储{[key]:state}形式的location哈希路径数据
// replaceLocation,// 哈希路径重新赋值,并使用window.sessionStorage存储{[key]:state}形式的location哈希路径数据
// listenBefore,// 将transitionTo方法绑定到hashChange事件上,添加前置钩子;事件触发时,执行钩子,及变更浏览器location及window.sessionStorage存储
// listen,// 将transitionTo方法绑定到hashChange事件上,添加后置钩子;事件触发时,执行钩子,及变更浏览器location及window.sessionStorage存储
// transitionTo,// 生成哈希路径
// createLocation,// 生成location数据
// }
var _createHashHistory = require('history/lib/createHashHistory');
var _createHashHistory2 = _interoprequiredefault(_createHashHistory);
// 装饰createHistory函数后,并调用createHistory创建history对象
var _createRouterHistory = require('./createRouterHistory');
var _createRouterHistory2 = _interoprequiredefault(_createRouterHistory);
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// 创建history对象
// {
// getUserConfirmation,// 默认值'_k',哈希路径中查询字符串的键,值为window.sessionStorage存储state的键
// hashType,// 默认值"slash",规定哈希路径hashPath书写形式
// getCurrentLocation,_createRouterHistory2.default)(_createHashHistory2.default);
module.exports = exports['default'];
'use strict';
exports.__esModule = true;
// 装饰createHistory函数后,并调用createHistory创建history对象
exports.default = function (createHistory) {
var history = void 0;
if (canUseDOM) history = (0,_useRouterHistory2.default)(createHistory)();
return history;
};
// useRouterHistory(createHistory),装饰createHistory函数,操作location数据对象时query查询字符串序列化对象、basename基础路径
var _useRouterHistory = require('./useRouterHistory');
var _useRouterHistory2 = _interoprequiredefault(_useRouterHistory);
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
module.exports = exports['default'];
createMemoryHistory.js
'use strict';
exports.__esModule = true;
exports.default = createMemoryHistory;
// 装饰createHistory函数,操作location数据对象时分离query查询字符串对象,返回装饰函数
var _useQueries = require('history/lib/useQueries');
var _useQueries2 = _interoprequiredefault(_useQueries);
// 装饰createHistory函数,操作location数据对象时分离basename,返回装饰函数
var _useBasename = require('history/lib/useBasename');
var _useBasename2 = _interoprequiredefault(_useBasename);
// createMemoryHistory(options),使用js闭包缓存特点构建{key:state}存储机制,存储在闭包变量storage中
// options.entries options为路径对象{path,key,state}构成的数组时,将options赋值给options.entries;
// options为字符串时,将[options]赋值给options.entries
// options.current 默认取options.entries.length-1
// 返回值
// {
// entries,// {path,state}对象构成的路径数据数组
// getCurrentLocation,// entries、storage添加一条路径记录
// replaceLocation,// entries、storage替换一条路径记录
// listenBefore,// 添加后置钩子
// listen,// 添加后置钩子
// transitionTo,// 变更页面路径、更新storage、执行钩子函数,更新allKeys,可作为页面路径变更时的回调
// push,// transitionTo方法变更页面路径、更新storage、执行钩子函数,allKeys添加一条路径记录,可作为页面路径变更时的回调
// replace,// transitionTo方法变更页面路径、更新storage、执行钩子函数,allKeys替换一条路径记录,可作为页面路径变更时的回调
// go,// 变更页面路径、更新storage、执行钩子函数,更新allKeys
// goBack,// 生成location数据
// }
var _createMemoryHistory = require('history/lib/createMemoryHistory');
var _createMemoryHistory2 = _interoprequiredefault(_createMemoryHistory);
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function createMemoryHistory(options) {
var memoryHistory = (0,_createMemoryHistory2.default)(options);
var createHistory = function createHistory() {
return memoryHistory;
};
var history = (0,_useQueries2.default)((0,_useBasename2.default)(createHistory))(options);
return history;
}
module.exports = exports['default'];
10.match.js、applyRouterMiddleware.js、useRouterHistory.js
match.js、useRouterHistory.js作为工具函数;此外RouterContent.js也作为工具函数,对外提供接口,经用户装饰后,如输出WrappedRouterContent组件,向Router元素的props.render=function(props) {return React.createElement(WrappedRouterContent,props);},将改变Route元素下设定的待加载组件的渲染机制。具体用法缺省。。。
applyRouterMiddleware.js模块用于挂载中间件,用于装饰Route元素下设定的待挂载的Component元素,及RouterContent元素,获取RouterContent元素的创建函数,可作为Router元素的props.render传入,以影响待加载组件的绘制。中间件middleware的属性形式须含有renderRouteComponent、renderRouterContext方法。其中,renderRouteComponent(component,props)由props装饰Route元素下挂载的Component元素,参数component即Component元素(react-element形式);renderRouterContext(routerContent,renderProps)由renderProps封装RouterContent元素,参数routerContent即RouterContent元素。
match.js模块在服务器端异步渲染routes时使用,提供match( _ref={history,location},function(error,renderProps) )方法。获取重定向路径数据redirectLocation、或_ref.location相应的renderProps={outer,matchContext,components},作为参数传入用户设置的回调函数中,完成页面重定向或组件重绘。具体用法缺省。。。
useRouterHistory.js模块提供useRouterHistory(createHistory)方法,用于装饰createHistory函数,location数据对象分离出query查询字符串序列化对象、basename基础路径。
match.js源码
'use strict';
exports.__esModule = true;
// 类似jquery.extend,将带靠背对象浅拷贝给目标对象
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source,key)) {
target[key] = source[key];
}
}
}
return target;
};
var _Actions = require('history/lib/Actions');
// node模块;invariant(condition,f替换报错模板字符串format,抛出错误
// _invariant2.default引用_invariant,即node模块invariant
var _invariant = require('invariant');
var _invariant2 = _interoprequiredefault(_invariant);
var _createMemoryHistory = require('./createMemoryHistory');
var _createMemoryHistory2 = _interoprequiredefault(_createMemoryHistory);
// 创建transitionManager对象,用于监听、取消监听popstate、hashChange事件
var _createTransitionManager = require('./createTransitionManager');
var _createTransitionManager2 = _interoprequiredefault(_createTransitionManager);
// 提供createRouterObject、assignRouterState方法
// createRouterObject(history,_ref)拷贝_ref的location、params、routes属性给router
// 次参_ref通常是Router组件当前的state
var _RouteUtils = require('./RouteUtils');
var _RouterUtils = require('./RouterUtils');
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// obj的属性或方法在keys中也有,不予拷贝
function _objectWithoutProperties(obj,i)) continue;
target[i] = obj[i];
}
return target;
};
// 服务器端异步渲染routes使用,match({history,renderProps))
// 通过_ref.history、_ref.routes创建transitionManager对象,并执行transitionManager.match方法
// transitionManager.match方法中,获取_ref.location引起重定向路径数据redirectLocation、更迭的state数据nextState
// 通过nextState={location,components}获得renderProps={router,components}
// callback回调函数最终将获得参数redirectLocation、renderProps,用于页面重定向或组件渲染
function match(_ref,callback) {
var history = _ref.history,routes = _ref.routes,location = _ref.location,options = _objectWithoutProperties(_ref,['history','routes','location']);
!(history || location) ? process.env.NODE_ENV !== 'production' ?
(0,'match needs a history or a location')
: (0,_invariant2.default)(false) : void 0;
history = history ? history : (0,_createMemoryHistory2.default)(options);
var transitionManager = (0,_createTransitionManager2.default)(history,_RouteUtils.createRoutes)(routes));
if (location) {
location = history.createLocation(location);
} else {
location = history.getCurrentLocation();
}
transitionManager.match(location,nextState) {
var renderProps = void 0;
if (nextState) {
var router = (0,nextState);
renderProps = _extends({},{
router: router,matchContext: { transitionManager: transitionManager,router: router }
});
}
callback(error,redirectLocation && history.createLocation(redirectLocation,_Actions.REPLACE),renderProps);
});
}
exports.default = match;
module.exports = exports['default'];
applyRouterMiddleware.js源码
'use strict';
exports.__esModule = true;
// 类似jquery.extend,将带靠背对象浅拷贝给目标对象
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source,key)) {
target[key] = source[key];
}
}
}
return target;
};
var _react = require('react');
var _react2 = _interoprequiredefault(_react);
// 负责渲染Router下子Route挂载的组件,由Router的state数据获取到激活的Route组件及其components待挂载的组件
// 通过底层的Route组件向上遍历父Route,创建待挂载渲染的props.component|components组件元素reactElement实现
var _RouterContext = require('./RouterContext');
var _RouterContext2 = _interoprequiredefault(_RouterContext);
// 封装warning模块,目的是使含'deprecated'的警告文案只提示一次
var _routerWarning = require('./routerWarning');
var _routerWarning2 = _interoprequiredefault(_routerWarning);
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// middleware.renderRouteComponent(component,props) 由props装饰Route元素下挂载的Component元素
// middleware.renderRouterContext(routerContent,renderProps) 由renderProps封装RouterContent元素
// 使用中间件装饰Route下待挂载的Component元素,及装饰RouterContent元素
// 返回创建装饰RouterContent元素的函数,可作为Router元素的props.render属性传入,改变Component元素的渲染方式
exports.default = function () {
for (var _len = arguments.length,middlewares = Array(_len),_key = 0; _key < _len; _key++) {
middlewares[_key] = arguments[_key];
}
// 中间件middleware须设置renderRouterContext、renderRouteComponent方法
if (process.env.NODE_ENV !== 'production') {
middlewares.forEach(function (middleware,index) {
process.env.NODE_ENV !== 'production' ?
(0,_routerWarning2.default)(middleware.renderRouterContext ||
middleware.renderRouteComponent,'The middleware specified at index ' + index
+ ' does not appear to be ' + 'a valid React Router middleware.')
: void 0;
});
}
var withContext = middlewares.map(function (middleware) {
return middleware.renderRouterContext;
}).filter(Boolean);
var withComponent = middlewares.map(function (middleware) {
return middleware.renderRouteComponent;
}).filter(Boolean);
// middleware.renderRouteComponent(component,props) 由props装饰Route元素下挂载的Component元素
// 返回用于创建Component元素的函数,该Component元素经middleware.renderRouteComponent封装后
// 返回函数function createElement(Component,props)将作为Route元素的props.createElement方法
var makeCreateElement = function makeCreateElement() {
var baseCreateElement = arguments.length > 0 && arguments[0] !== undefined ?
arguments[0] : _react.createElement;
return function (Component,props) {
return withComponent.reduceRight(function (prev@R_403_437@s,renderRouteComponent) {
return renderRouteComponent(prev@R_403_437@s,baseCreateElement(Component,props));
};
};
// middleware.renderRouterContext(routerContent,renderProps) 由renderProps封装RouterContent元素
// 返回用于创建RouterContext元素的函数,该RouterContext元素middleware.renderRouterContext封装后
// 返回函数function createElement(RouterContext,renderProps)将作为Router元素的props.render方法
return function (renderProps) {
return withContext.reduceRight(function (prev@R_403_437@s,renderRouterContext) {
return renderRouterContext(prev@R_403_437@s,renderProps);
},_react2.default.createElement(_RouterContext2.default,renderProps,{
createElement: makeCreateElement(renderProps.createElement)
})));
};
};
module.exports = exports['default'];
useRouterHistory.js源码
'use strict';
exports.__esModule = true;
exports.default = useRouterHistory;
// 装饰createHistory函数,操作location数据对象时分离query查询字符串对象,返回装饰函数
var _useQueries = require('history/lib/useQueries');
var _useQueries2 = _interoprequiredefault(_useQueries);
// 装饰createHistory函数,操作location数据对象时分离basename,返回装饰函数
var _useBasename = require('history/lib/useBasename');
var _useBasename2 = _interoprequiredefault(_useBasename);
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// 装饰createHistory函数,操作location数据对象时query查询字符串序列化对象、basename基础路径
function useRouterHistory(createHistory) {
return function (options) {
var history = (0,_useBasename2.default)(createHistory))(options);
return history;
};
}
module.exports = exports['default'];
11.内置工具函数
AsyncUtils.js
AsyncUtils.js提供loopAsync、mapAsync函数
loopAsync(turns,work,callback)函数设定以高阶函数work(currentTurn,next,done)操作长度为turns的数组(纯数据或函数集);turns迭代最大次数,work(currentTurn++,done)迭代执行函数,callback回调
mapAsync(array,callback)函数以work函数遍历执行array的数组项item,work的第三个参数函数为每次遍历时执行的回调;该回调用以获取callback的参数values,并判断遍历完成是否完成,完成后执行callback
'use strict';
exports.__esModule = true;
// loopAsync(turns,done)操作长度为turns的数组(纯数据或函数集)
// turns迭代最大次数,work(currentTurn++,done)迭代执行函数,callback回调
// mapAsync(array,callback)函数以work函数遍历执行array的数组项item,work的第三个参数函数为每次遍历时执行的回调
// 该回调用以获取callback的参数values,并判断遍历完成是否完成,完成后执行callback
var _AsyncUtils = require('./AsyncUtils');
// isPromise(obj)判断obj是否promise对象
var _PromiseUtils = require('./PromiseUtils');
// 获取激活Route下挂载的组件,并执行回调
function getComponentsForRoute(nextState,callback) {
if (route.component || route.components) {
callback(null,route.component || route.components);
return;
}
var getComponent = route.getComponent || route.getComponents;
if (getComponent) {
var componentReturn = getComponent.call(route,callback);
if ((0,_PromiseUtils.isPromise)(componentReturn)) componentReturn.then(function (component) {
return callback(null,component);
},callback);
} else {
callback();
}
}
// getComponents(nextState,callback)获取激活路径下挂载的多个组件,并执行回调
function getComponents(nextState,callback) {
(0,_AsyncUtils.mapAsync)(nextState.routes,function (route,index,callback) {
getComponentsForRoute(nextState,callback);
},callback);
}
exports.default = getComponents;
module.exports = exports['default'];
computeChangedRoutes.js
computeChangedRoutes.js模块提供computeChangedRoutes方法。
computeChangedRoutes(prevState,nextState)由前后两个路径数据prevState、nextState获取routes元素中参数待变更的changeRoutes、待移出的leaveRoutes、待进入的enterRoutes。
'use strict';
exports.__esModule = true;
// 通过histroy设定的路径格式获取真实路径的正则匹配规则,或获取真实路径的路径变量,或通过路径变量和路径格式获取真实路径
var _PatternUtils = require('./PatternUtils');
// 判断redux.store前后两个state数据prevState、nextState的params是否改变,实质是路径变量改变
function routeParamsChanged(route,prevState,nextState) {
if (!route.path) return false;
// 数组形式获取路径变量名
var paramNames = (0,_PatternUtils.getParamNames)(route.path);
return paramNames.some(function (paramName) {
return prevState.params[paramName] !== nextState.params[paramName];
});
}
// 参数prevState、nextState为redux.store前后两个state数据,state.routes是Route组件的配置,数组形式
// 获取prevState待改变的route项leaveRoutes,nextState中保持与prevState相同的route项changeRoutes
// nextState中与prevState相同的route项changeRoutes,或者route.path改变,或者state.params改变
function computeChangedRoutes(prevState,nextState) {
var prevRoutes = prevState && prevState.routes;// 数组形式变更前的Route组件配置
var nextRoutes = nextState.routes;// 数组形式变更后的Route组件配置,route.path属性为路径
// prevState跳转为nextState时,存储prevRoutes中route.path或state.params待变更的route项
// prevState父route添加到leaveRoutes后,后续route全添加到leaveRoutes中
// 页面上即跳转到leaveRoutes[0]平级的Route组件中或leaveRoutes[0]的state.params作变更
var leaveRoutes = void 0,// prevState跳转为nextState时,nextState中和prevState中相同的route项
changeRoutes = void 0,// 存储prevRoutes跳转为nextRoutes时,nextState中和prevState中不同的route项,或者route.path改变,或者state.params改变
enterRoutes = void 0;
if (prevRoutes) {
(function () {
var parentIsLeaving = false;
leaveRoutes = prevRoutes.filter(function (route) {
// 父route已作改变,子route全包含
if (parentIsLeaving) {
return true;
} else {
var isLeaving = nextRoutes.indexOf(route) === -1 || routeParamsChanged(route,nextState);
if (isLeaving) parentIsLeaving = true;
return isLeaving;
}
});
leaveRoutes.reverse();// 反转为从子route到父route
enterRoutes = [];
changeRoutes = [];
nextRoutes.forEach(function (route) {
// nextRoutes所有的route不在prevRoutes中,route.path改变
// 页面上即为跳转到leaveRoutes[0]的平级Route组件后所有后续route
var isNew = prevRoutes.indexOf(route) === -1;
// nextRoutes所有的route也在leaveRoutes中,只可能leaveRoutes[0~i]的state.params作变更
var paramsChanged = leaveRoutes.indexOf(route) !== -1;
if (isNew || paramsChanged){
enterRoutes.push(route);
// nextRoutes所有的route在prevRoutes中,却不在leaveRoutes中
// 即nextRoutes和prevRoutes相同的route部分
}else{
changeRoutes.push(route);
};
});
})();
} else {
leaveRoutes = [];
changeRoutes = [];
enterRoutes = nextRoutes;
}
return {
leaveRoutes: leaveRoutes,changeRoutes: changeRoutes,enterRoutes: enterRoutes
};
}
exports.default = computeChangedRoutes;
module.exports = exports['default'];
ContextUtils.js
ContextUtils.js模块用于向下层组件提供监听Router元素的state变更,继而触发下层组件的setState方法执行并重绘。主要应用在Link、IndexLink、WithRouter(WrappedComponent)封装的WrappedComponent元素。
'use strict';
exports.__esModule = true;
// 类似jquery.extend,将带靠背对象浅拷贝给目标对象
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source,WrappedComponent);
}
module.exports = exports['default'];
getComponents.js
getComponents.js提供getComponent方法。
getComponents(nextState,callback)获取激活路径下挂载的多个组件,并执行回调。
'use strict';
exports.__esModule = true;
// loopAsync(turns,callback);
}
exports.default = getComponents;
module.exports = exports['default'];
getRouteParams.js
getRouteParams.js提供getRouteParams方法。
getRouteParams(route,params)由特定route及页面路径变量params获取该route下相关的变量信息。
'use strict';
exports.__esModule = true;
// 通过histroy设定的路径格式获取真实路径的正则匹配规则,或获取真实路径的路径变量,或通过路径变量和路径格式获取真实路径
var _PatternUtils = require('./PatternUtils');
// 通过route获取路径变量的键,再注入params相应键的值,意为当前route路径的变量
function getRouteParams(route,params) {
var routeParams = {};
if (!route.path) return routeParams;
(0,_PatternUtils.getParamNames)(route.path).forEach(function (p) {
if (Object.prototype.hasOwnProperty.call(params,p)) {
routeParams[p] = params[p];
}
});
return routeParams;
}
exports.default = getRouteParams;
module.exports = exports['default'];
InternalPropTypes.js
InternalPropTypes.js用于校验props。
'use strict';
exports.__esModule = true;
exports.routes = exports.route = exports.components = exports.component = exports.history = undefined;
exports.falsy = falsy;
var _react = require('react');
var func = _react.PropTypes.func,object = _react.PropTypes.object,arrayOf = _react.PropTypes.arrayOf,oneOfType = _react.PropTypes.oneOfType,element = _react.PropTypes.element,shape = _react.PropTypes.shape,string = _react.PropTypes.string;
function falsy(props,propName,componentName) {
if (props[propName]) return new Error('<' + componentName + '> should not have a "' + propName + '" prop');
}
var history = exports.history = shape({
listen: func.isrequired,push: func.isrequired,replace: func.isrequired,go: func.isrequired,goBack: func.isrequired,goForward: func.isrequired
});
var component = exports.component = oneOfType([func,string]);
var components = exports.components = oneOfType([component,object]);
var route = exports.route = oneOfType([object,element]);
var routes = exports.routes = oneOfType([route,arrayOf(route)]);
PropTypes.js
'use strict';
exports.__esModule = true;
exports.locationShape = exports.routerShape = undefined;
var _react = require('react');
var func = _react.PropTypes.func,string = _react.PropTypes.string;
var routerShape = exports.routerShape = shape({
push: func.isrequired,goForward: func.isrequired,setRouteLeaveHook: func.isrequired,isActive: func.isrequired
});
var locationShape = exports.locationShape = shape({
pathname: string.isrequired,search: string.isrequired,action: string.isrequired,key: string
});
isActive.js
isActive.js模块提供isActive方法。
isActive(_ref,params)用于校验特定路径数据_ref是否同当前页面路径pathname完全相等(indexOnly为真时);或者匹配激活组件设定的路由规则(indexOnly为否时)。
'use strict';
exports.__esModule = true;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
exports.default = isActive;
// 通过histroy设定的路径格式获取真实路径的正则匹配规则,或获取真实路径的路径变量,或通过路径变量和路径格式获取真实路径
var _PatternUtils = require('./PatternUtils');
// 比较a,b值是否相等
function deepEqual(a,b) {
if (a == b) return true;
if (a == null || b == null) return false;
if (Array.isArray(a)) {
return Array.isArray(b) && a.length === b.length && a.every(function (item,index) {
return deepEqual(item,b[index]);
});
}
if ((typeof a === 'undefined' ? 'undefined' : _typeof(a)) === 'object') {
for (var p in a) {
if (!Object.prototype.hasOwnProperty.call(a,p)) {
continue;
}
if (a[p] === undefined) {
if (b[p] !== undefined) {
return false;
}
} else if (!Object.prototype.hasOwnProperty.call(b,p)) {
return false;
} else if (!deepEqual(a[p],b[p])) {
return false;
}
}
return true;
}
return String(a) === String(b);
}
// 比较pathname同当前路径currentPathname是否相同
function pathIsActive(pathname,currentPathname) {
if (currentPathname.charAt(0) !== '/') {
currentPathname = '/' + currentPathname;
}
if (pathname.charAt(pathname.length - 1) !== '/') {
pathname += '/';
}
if (currentPathname.charAt(currentPathname.length - 1) !== '/') {
currentPathname += '/';
}
return currentPathname === pathname;
}
// 判断路径数据pathname是否同当前页面路径下激活的routes、路径变量params相匹配
function routeIsActive(pathname,params) {
var remainingPathname = pathname,paramNames = [],paramValues = [];
for (var i = 0,len = routes.length; i < len; ++i) {
var route = routes[i];
var pattern = route.path || '';
if (pattern.charAt(0) === '/') {
remainingPathname = pathname;
paramNames = [];
paramValues = [];
}
if (remainingPathname !== null && pattern) {
var matched = (0,_PatternUtils.matchPattern)(pattern,remainingPathname);
if (matched) {
remainingPathname = matched.remainingPathname;
paramNames = [].concat(paramNames,matched.paramNames);
paramValues = [].concat(paramValues,matched.paramValues);
} else {
remainingPathname = null;
}
if (remainingPathname === '') {
return paramNames.every(function (paramName,index) {
return String(paramValues[index]) === String(params[paramName]);
});
}
}
}
return false;
}
// 判断query路径变量是否同当前页面路径变量activeQuery匹配,query为null时,不需要匹配activeQuery
function queryIsActive(query,activeQuery) {
if (activeQuery == null) return query == null;
if (query == null) return true;
return deepEqual(query,activeQuery);
}
// 判断路径数据_ref同当前路径是否相同
// 参数_ref待校验的路径数据location
// indexOnly为真值时路径pathname需要完全匹配,否则pathname匹配激活的route设下的路由规则即可
// 参数currentLocation,params当前页面相关数据
function isActive(_ref,params) {
var pathname = _ref.pathname,query = _ref.query;
if (currentLocation == null) return false;
if (pathname.charAt(0) !== '/') {
pathname = '/' + pathname;
}
if (!pathIsActive(pathname,currentLocation.pathname)) {
if (indexOnly || !routeIsActive(pathname,params)) {
return false;
}
}
return queryIsActive(query,currentLocation.query);
}
module.exports = exports['default'];
matchRoutes.js
matchRoutes.js提供matchRoutes方法。
matchRoutes(routes,callback,remainingPathname)用于获取获取当前路径下被激活的Route组件以及路径变量信息params,{routes,params}形式,并且将{routes,params}传入回调callback函数作为参数。
'use strict';
exports.__esModule = true;
// 类似jquery.extend,将带靠背对象浅拷贝给目标对象
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source,key)) {
target[key] = source[key];
}
}
}
return target;
};
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
exports.default = matchRoutes;
// loopAsync(turns,callback)函数以work函数遍历执行array的数组项item,work的第三个参数函数为每次遍历时执行的回调
// 该回调用以获取callback的参数values,并判断遍历完成是否完成,完成后执行callback
var _AsyncUtils = require('./AsyncUtils');
// isPromise(obj)判断obj是否promise对象
var _PromiseUtils = require('./PromiseUtils');
// 通过histroy设定的路径格式获取真实路径的正则匹配规则,或获取真实路径的路径变量,或通过路径变量和路径格式获取真实路径
var _PatternUtils = require('./PatternUtils');
// 封装warning模块,目的是使含'deprecated'的警告文案只提示一次
var _routerWarning = require('./routerWarning');
var _routerWarning2 = _interoprequiredefault(_routerWarning);
// 提供一系列方法,目的是将Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
var _RouteUtils = require('./RouteUtils');
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// 同步或者异步方式获取childRoutes,通过route.childRoutes属性或getChildRoutes方法
function getChildRoutes(route,paramNames,paramValues,callback) {
if (route.childRoutes) {
return [null,route.childRoutes];
}
if (!route.getChildRoutes) {
return [];
}
var sync = true,result = void 0;
var partialNextState = {
location: location,params: createParams(paramNames,paramValues)
};
var childRoutesReturn = route.getChildRoutes(partialNextState,childRoutes) {
// _RouteUtils.createRoutes将Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
childRoutes = !error && (0,_RouteUtils.createRoutes)(childRoutes);
if (sync) {
result = [error,childRoutes];
return;
}
callback(error,childRoutes);
});
if ((0,_PromiseUtils.isPromise)(childRoutesReturn)) childRoutesReturn.then(function (childRoutes) {
// _RouteUtils.createRoutes将Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
return callback(null,_RouteUtils.createRoutes)(childRoutes));
},callback);
sync = false;
return result; // Might be undefined.
}
// IndexRoute处于路径激活状态,肯定是最末一个激活Route的子组件
// 激活的Route中添加相应的IndexRoute,意义还是获取待加载的组件
function getIndexRoute(route,callback) {
if (route.indexRoute) {
callback(null,route.indexRoute);
} else if (route.getIndexRoute) {// 父Route以props配置书写getIndexRoute函数获取IndexRoute,供回调使用
var partialNextState = {
location: location,paramValues)
};
var indexRoutesReturn = route.getIndexRoute(partialNextState,indexRoute) {
// _RouteUtils.createRoutes将Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
callback(error,!error && (0,_RouteUtils.createRoutes)(indexRoute)[0]);
});
// 支持异步,相关问题???
if ((0,_PromiseUtils.isPromise)(indexRoutesReturn)) indexRoutesReturn.then(function (indexRoute) {
// _RouteUtils.createRoutes将Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
return callback(null,_RouteUtils.createRoutes)(indexRoute)[0]);
},callback);
} else if (route.childRoutes) {
(function () {
// Route组件没有设置props.path,获取其下IndexRoute组件,目的是获悉待加载的组件
var pathless = route.childRoutes.filter(function (childRoute) {
return !childRoute.path;
});
(0,_AsyncUtils.loopAsync)(pathless.length,function (index,done) {
getIndexRoute(pathless[index],indexRoute) {
if (error || indexRoute) {
var routes = [pathless[index]].concat(Array.isArray(indexRoute) ? indexRoute : [indexRoute]);
done(error,routes);
} else {
next();
}
});
},function (err,routes) {
callback(null,routes);
});
})();
} else {
callback();
}
}
// 合并路径变量对象
function assignParams(params,paramValues) {
return paramNames.reduce(function (params,paramName,index) {
var paramValue = paramValues && paramValues[index];
if (Array.isArray(params[paramName])) {
params[paramName].push(paramValue);
} else if (paramName in params) {
params[paramName] = [params[paramName],paramValue];
} else {
params[paramName] = paramValue;
}
return params;
},params);
}
// 获取路径变量对象
function createParams(paramNames,paramValues) {
return assignParams({},paramValues);
}
// 参数route是逐层遍历Route组件时该组件的配置项
// Route组件遍历通过反复调用matchRoutes函数实现,每次调用将遍历平级的Route组件
// 参数location当前页面路径数据
// 参数remainingPathname用于递归调用matchRouteDeep记录未被正则匹配的路径
// 参数paramNames、paramValues用于递归调用matchRouteDeep记录已获取的路径变量键及其值
// callback用于区别执行_AsyncUtils.loopAsync函数机制的done、next迭代函数
function matchRouteDeep(route,remainingPathname,callback) {
var pattern = route.path || '';// route路径的正则匹配规则
// Router下第一层Route设置的path参数以"/"起始,第二层开始不以"/"起始
// 目的是使remainingPathname未匹配路径在第一层Route下为当前页面路径,第二层起始位当前页面路径剩余未匹配部分
// 通过matchRouteDeep函数remainingPathname传参实现
if (pattern.charAt(0) === '/') {
remainingPathname = location.pathname;// 当前页面路径
paramNames = [];
paramValues = [];
}
if (remainingPathname !== null && pattern) {
// 获取route路径变量信息,混入paramNames、paramValues
try {
// _PatternUtils.matchPattern(pattern,remainingPathname),remainingPathname为实际页面路径未匹配部分
// 获取真实路径尾部未匹配的路径、路径变量名数组、路径变量值数组
var matched = (0,matched.paramValues);
} else {
remainingPathname = null;
}
} catch (error) {
callback(error);
}
// 当前页面路径location数据已经被遍历的route匹配完毕,即当前路径下所有激活的Route
// IndexRoute处于路径激活状态,肯定是最末一个激活Route的子组件
if (remainingPathname === '') {
var _ret2 = function () {
var match = {
routes: [route],paramValues)
};
// 激活的Route中添加相应的IndexRoute,意义还是获取待加载的组件
getIndexRoute(route,indexRoute) {
if (error) {
// Router组件下Route为单层形式,callback为createTransitionManager模块调用matchRoutes时设置
// Router组件下Route为多层形式,callback为onChildRoutes函数调用matchRoutes时设置
callback(error);
} else {
if (Array.isArray(indexRoute)) {
var _match$routes;
process.env.NODE_ENV !== 'production' ?
(0,_routerWarning2.default)(indexRoute.every(function (route) {
return !route.path;
}),'Index routes should not have paths') : void 0;
(_match$routes = match.routes).push.apply(_match$routes,indexRoute);
} else if (indexRoute) {
process.env.NODE_ENV !== 'production' ?
(0,_routerWarning2.default)(!indexRoute.path,'Index routes should not have paths') : void 0;
match.routes.push(indexRoute);
}
// Router组件下Route为单层形式,callback为createTransitionManager模块调用matchRoutes时设置
// Router组件下Route为多层形式,callback为onChildRoutes函数调用matchRoutes时设置
callback(null,match);
}
});
return {
v: void 0
};
}();
if ((typeof _ret2 === 'undefined' ? 'undefined' : _typeof(_ret2)) === "object")
return _ret2.v;
}
}
// Router组件下Route为多层形式
// 通过调用matchRoutes函数遍历子Route组件的配置项,用以通过未匹配路径获取激活的Route,并获得match
if (remainingPathname != null || route.childRoutes) {
var onChildRoutes = function onChildRoutes(error,childRoutes) {
if (error) {
callback(error);
} else if (childRoutes) {
matchRoutes(childRoutes,match) {
if (error) {
callback(error);
} else if (match) {
// Router组件下Route为多层形式,match.routes已经通过_ret2添加了子route,再添加当前父route
match.routes.unshift(route);
// callback为createTransitionManager模块调用matchRoutes时设置
callback(null,match);
} else {
// callback为createTransitionManager模块调用matchRoutes时设置
callback();
}
},paramValues);
} else {
callback();
}
};
// getChildRoutes函数同步或者异步方式获取childRoutes,通过route.childRoutes属性或route.getChildRoutes方法
var result = getChildRoutes(route,onChildRoutes);
if (result) {
onChildRoutes.apply(undefined,result);
}
} else {
callback();
}
}
// matchRoutes(routes,callback)外部调用,
// 获取当前路径下被激活的Route组件以及路径变量信息params,{routes,params}传入回调callback函数作为参数
// matchRoutes(routes,remainingPathname)内部调用,遍历子Route组件,获取激活子Route信息
function matchRoutes(routes,remainingPathname) {
var paramNames = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];
var paramValues = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : [];
if (remainingPathname === undefined) {
if (location.pathname.charAt(0) !== '/') {
location = _extends({},{
pathname: '/' + location.pathname
});
}
remainingPathname = location.pathname;
}
// _AsyncUtils.loopAsync(turns,done)操作长度为turns的数组(纯数据或函数集)
// turns迭代最大次数,work(currentTurn++,done)迭代执行函数,callback回调
(0,_AsyncUtils.loopAsync)(routes.length,done) {
matchRouteDeep(routes[index],match) {
if (error || match) {
done(error,match);
} else {
next();
}
});
},callback);
}
module.exports = exports['default'];
PatternUtils.js 路径解析相关
PatternUtils.js提供compilePattern、matchPattern、getParamNames、getParams、formatPattern方法。
compilePattern(pattern) 通过路由匹配规则pattern获取{pattern,regexpSource,tokens}正则匹配规则、路径变量、原始路径、拆分路径等。
matchPattern(pattern,pathname) 获取pathname下未符合路由规则的remainingPathname,数组形式的路径变量键与值,返回值为{remainingPathname,paramValue}形式。
getParamNames(pattern)数组形式获取路径变量的键。
getParams(pattern,pathname)以键值对获取路径变量。
formatPattern(pattern,params)将路径变量填入路由规则pattern中,获取实际路径输出。
'use strict';
exports.__esModule = true;
exports.compilePattern = compilePattern;
exports.matchPattern = matchPattern;
exports.getParamNames = getParamNames;
exports.getParams = getParams;
exports.formatPattern = formatPattern;
// node模块;invariant(condition,f替换报错模板字符串format,抛出错误
// _invariant2.default引用_invariant,即node模块invariant
var _invariant = require('invariant');
var _invariant2 = _interoprequiredefault(_invariant);
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// ".*+?^${}()|[]\"等特殊于正则的字符加"\"
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g,'\\$&');
}
// 参数pattern为route中histroy设置的页面路径形式,书写形式为/path/:id形式,id为单页应用中的路径变量
// 通过路径获取正则规则和路径变量名,凭此得到当前页面的路径变量值
function _compilePattern(pattern) {
var regexpSource = '';
var paramNames = [];
var tokens = [];
var match = void 0,lastIndex = 0,matcher = /:([a-zA-Z_$][a-zA-Z0-9_$]*)|\*\*|\*|\(|\)/g;
while (match = matcher.exec(pattern)) {
if (match.index !== lastIndex) {
tokens.push(pattern.slice(lastIndex,match.index));
regexpSource += escapeRegExp(pattern.slice(lastIndex,match.index));
}
if (match[1]) {
regexpSource += '([^/]+)';
paramNames.push(match[1]);
} else if (match[0] === '**') {
regexpSource += '(.*)';
paramNames.push('splat');
} else if (match[0] === '*') {
regexpSource += '(.*?)';
paramNames.push('splat');
} else if (match[0] === '(') {// 非获取匹配,'(?:'及')?'可以满足或不满足匹配条件
regexpSource += '(?:';
} else if (match[0] === ')') {
regexpSource += ')?';
}
tokens.push(match[0]);
lastIndex = matcher.lastIndex;
}
if (lastIndex !== pattern.length) {
tokens.push(pattern.slice(lastIndex,pattern.length));
regexpSource += escapeRegExp(pattern.slice(lastIndex,pattern.length));
}
return {
pattern: pattern,// 完整原始路径
regexpSource: regexpSource,// 正则规则,([^/]+)或(.*)或(.*?)获取当前页面路径变量的值
paramNames: paramNames,// 路径变量名,(.*)或(.*?)匹配为"splat"
tokens: tokens// 路径变量和非路径变量部分拆分为数组
};
}
// 以对象形式存储解析为正则匹配规则的路径,键为路径名,值为正则匹配规则、路径变量、原始路径、拆分路径等
var CompiledPatternsCache = Object.create(null);
// 通过histroy设置的页面路径形式获取正则匹配规则、路径变量、原始路径、拆分路径等,并存储到CompiledPatternsCache中
function compilePattern(pattern) {
if (!CompiledPatternsCache[pattern]) CompiledPatternsCache[pattern] = _compilePattern(pattern);
return CompiledPatternsCache[pattern];
}
// 参数pattern为histroy设置的页面路径形式,pathname为实际页面路径
// 获取真实路径尾部未匹配的路径、路径变量名数组、路径变量值数组
function matchPattern(pattern,pathname) {
if (pattern.charAt(0) !== '/') {
pattern = '/' + pattern;
}
var _compilePattern2 = compilePattern(pattern),regexpSource = _compilePattern2.regexpSource,paramNames = _compilePattern2.paramNames,tokens = _compilePattern2.tokens;
if (pattern.charAt(pattern.length - 1) !== '/') {
regexpSource += '/?'; // Allow optional path separator at end.
}
// 参数pattern以"*"路径变量结尾,正则匹配规则添加"$",获取实际路径后所有内容
if (tokens[tokens.length - 1] === '*') {
regexpSource += '$';
}
var match = pathname.match(new RegExp('^' + regexpSource,'i'));
if (match == null) {
return null;
}
var matchedPath = match[0];
var remainingPathname = pathname.substr(matchedPath.length);// 后续未匹配正则的路径
if (remainingPathname) {
// route中histroy设置的pattern以"/"结尾,匹配到真是路径的结尾
// route中histroy设置的pattern不以"/"结尾,不能匹配到真是路径的结尾
if (matchedPath.charAt(matchedPath.length - 1) !== '/') {
return null;
}
// If there is a remaining pathname,treat the path separator as part of
// the remaining pathname for properly continuing the match.
remainingPathname = '/' + remainingPathname;
}
return {
remainingPathname: remainingPathname,// 后续未匹配正则的路径
paramNames: paramNames,// 路径变量名,(.*)或(.*?)匹配为"splat"
paramValues: match.slice(1).map(function (v) {// 路径变量对应的值
return v && decodeURIComponent(v);// 原生decodeURIComponent函数,url解码
})
};
}
// 通过histroy设置的页面路径形式获取路径变量名
function getParamNames(pattern) {
return compilePattern(pattern).paramNames;
}
// 参数pattern为histroy设置的页面路径形式,pathname为实际页面路径
// 以键值对形式获取路径变量
function getParams(pattern,pathname) {
var match = matchPattern(pattern,pathname);
if (!match) {
return null;
}
var paramNames = match.paramNames,paramValues = match.paramValues;
var params = {};
paramNames.forEach(function (paramName,index) {
params[paramName] = paramValues[index];
});
return params;
}
// 参数pattern为histroy设置的页面路径形式,params键值对形式路径变量
// 获取真实路径输出
function formatPattern(pattern,params) {
params = params || {};
var _compilePattern3 = compilePattern(pattern),tokens = _compilePattern3.tokens;
var parenCount = 0,// 更新parenHistory记录()的个数
pathname = '',// 待拼接的路径
splatIndex = 0,// 通配符的个数
parenHistory = [];// 数组形式存储()间内容
var token = void 0,paramName = void 0,paramValue = void 0;
for (var i = 0,len = tokens.length; i < len; ++i) {
token = tokens[i];
if (token === '*' || token === '**') {
paramValue = Array.isArray(params.splat) ? params.splat[splatIndex++] : params.splat;
// pattern中路径格式通配符*不被()包裹,paramValue不能为空
!(paramValue != null || parenCount > 0) ? process.env.NODE_ENV !== 'production' ?
(0,'Missing splat #%s for path "%s"',splatIndex,pattern) :
(0,_invariant2.default)(false) : void 0;
if (paramValue != null) pathname += encodeURI(paramValue);// 原生encodeURI函数,url编码
} else if (token === '(') {
parenHistory[parenCount] = '';
parenCount += 1;
} else if (token === ')') {
var parenText = parenHistory.pop();
parenCount -= 1;
// 多个(),将后一个")"、")"内容压入前一个
if (parenCount){
parenHistory[parenCount - 1] += parenText;
// 所有起始的"("都找到匹配的")",使用parenHistory记录的()间内容,更新pathname
}else{
pathname += parenText;
}
} else if (token.charAt(0) === ':') {
paramName = token.substring(1);
paramValue = params[paramName];
// pattern中路径格式:id不被()包裹,paramValue不能为空
!(paramValue != null || parenCount > 0) ? process.env.NODE_ENV !== 'production' ?
(0,'Missing "%s" parameter for path "%s"',_invariant2.default)(false) : void 0;
if (paramValue == null) {
// pattern中路径格式:id被()包裹,paramValue无值,跳到相应的")"后
if (parenCount) {
parenHistory[parenCount - 1] = '';
var curTokenIdx = tokens.indexOf(token);// ":id"所在序号
var tokensSubset = tokens.slice(curTokenIdx,tokens.length);// 以":id"起始的后续tokens
var nextParenIdx = -1;// 用以获取")"在tokensSubset中的序号值
for (var _i = 0; _i < tokensSubset.length; _i++) {
if (tokensSubset[_i] == ')') {
nextParenIdx = _i;
break;
}
}
// "("缺少与之匹配的")",报错
!(nextParenIdx > 0) ? process.env.NODE_ENV !== 'production' ?
(0,'Path "%s" is missing end paren at segment "%s"',pattern,tokensSubset.join('')) :
(0,_invariant2.default)(false) : void 0;
// 对应的”)”在tokens中的序号值,++i跳到")"后
i = curTokenIdx + nextParenIdx - 1;
}
// pattern中路径格式:id被()包裹,paramValue有值
} else if (parenCount){
parenHistory[parenCount - 1] += encodeURIComponent(paramValue);
// pattern中路径格式:id不被()包裹,paramValue有值
}else{
pathname += encodeURIComponent(paramValue);
}
} else {
if (parenCount){
parenHistory[parenCount - 1] += token;
}else{
pathname += token;
}
}
}
// "("缺少与之匹配的")",报错
!(parenCount <= 0) ? process.env.NODE_ENV !== 'production' ?
(0,'Path "%s" is missing end paren',pattern) :
(0,_invariant2.default)(false) : void 0;
return pathname.replace(/\/+/g,'/');
}
PromiseUtils.js
PromiseUtils.js模块提供isPromise方法。
'use strict';
exports.__esModule = true;
exports.isPromise = isPromise;
// isPromise(obj)判断obj是否promise对象
function isPromise(obj) {
return obj && typeof obj.then === 'function';
}
'use strict';
exports.__esModule = true;
exports.default = routerWarning;
exports._resetWarned = _resetWarned;
// node模块;warning(condition,args) 开发环境下,condition为否值时,使用args替换报错字符串模板format输出
// _warning2.default引用_warning,即node模块warning
var _warning = require('warning');
var _warning2 = _interoprequiredefault(_warning);
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var warned = {};// 存储已经提示过且不含'deprecated'的警告文案;不含'deprecated'的警告文案只提示一次
// 封装warning模块,目的是使含'deprecated'的警告文案只提示一次
function routerWarning(falseToWarn,message) {
if (message.indexOf('deprecated') !== -1) {
if (warned[message]) {
return;
}
warned[message] = true;
}
message = '[react-router] ' + message;
for (var _len = arguments.length,args = Array(_len > 2 ? _len - 2 : 0),_key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
_warning2.default.apply(undefined,[falseToWarn,message].concat(args));
}
function _resetWarned() {
warned = {};
}
RouterUtils.js
RouterUtils.js提供createRouterObject、assignRouterState方法。
createRouterObject(history,state)创建router对象,用于@R_202_404@面、监听事件、获取当前页面路径数据等。
assignRouterState(router,_ref)更新router对象的路径数据,通常是页面跳转后、Router的state变更前即时更正为最新值。
"use strict";
exports.__esModule = true;
// 类似jquery.extend,将带靠背对象浅拷贝给目标对象
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source,routes = _ref.routes;
router.location = location;
router.params = params;
router.routes = routes;
return router;
}
RouteUtil.js 用于将JSX形式添加的Route元素转变为Router的props.routes数据,便于后续操作。
'use strict';
exports.__esModule = true;
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source,key)) { target[key] = source[key]; } } } return target; };
exports.isReactChildren = isReactChildren;
exports.createRouteFromReactElement = createRouteFromReactElement;
exports.createRoutesFromReactChildren = createRoutesFromReactChildren;
exports.createRoutes = createRoutes;
var _react = require('react');
var _react2 = _interoprequiredefault(_react);
// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interoprequiredefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// 校验元素是否reactElement
function isValidChild(object) {
return object == null || _react2.default.isValidElement(object);
}
// 校验单对象形式或数组形式的元素是否reactElement
function isReactChildren(object) {
return isValidChild(object) || Array.isArray(object) && object.every(isValidChild);
}
// 获取route元素的props
function createRoute(defaultProps,props) {
return _extends({},defaultProps,props);
}
// 通过route元素获取其props,作为router元素的props.routes|children配置
function createRouteFromReactElement(element) {
var type = element.type;
var route = createRoute(type.defaultProps,element.props);
if (route.children) {
// IndexRoute必然作为Route的子组件,同时Route也可能是Route的子组件
// IndexRoute情形,父route元素获得route.childRoutes属性的同时也得到route.indexRoute
// route情形,父route元素获得route.childRoutes属性
var childRoutes = createRoutesFromReactChildren(route.children,route);
if (childRoutes.length) route.childRoutes = childRoutes;
delete route.children;
}
return route;
}
/**
* import { Route,createRoutesFromReactChildren } from 'react-router'
*
* const routes = createRoutesFromReactChildren(
* <Route component={App}>
* <Route path="home" component={Dashboard}/>
* <Route path="news" component={NewsFeed}/>
* </Route>
* )
*/
// Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
function createRoutesFromReactChildren(children,parentRoute) {
var routes = [];
_react2.default.Children.forEach(children,function (element) {
if (_react2.default.isValidElement(element)) {
if (element.type.createRouteFromReactElement) {
var route = element.type.createRouteFromReactElement(element,parentRoute);
if (route) routes.push(route);
} else {
routes.push(createRouteFromReactElement(element));
}
}
});
return routes;
}
// Router中通过props.children添加的Route转化成props.routes形式,各route元素的props配置
// 参数routes为Router组件下第一层Route组件
function createRoutes(routes) {
if (isReactChildren(routes)) {
routes = createRoutesFromReactChildren(routes);
} else if (routes && !Array.isArray(routes)) {
routes = [routes];
}
return routes;
}
TransitionUtils.js 用于实现Route元素的props.onEnter | onLeave | onChange 方法的执行。
'use strict';
exports.__esModule = true;
exports.runEnterHooks = runEnterHooks;
exports.runChangeHooks = runChangeHooks;
exports.runLeaveHooks = runLeaveHooks;
// loopAsync(turns,callback)函数以work函数遍历执行array的数组项item,work的第三个参数函数为每次遍历时执行的回调
// 该回调用以获取callback的参数values,并判断遍历完成是否完成,完成后执行callback
var _AsyncUtils = require('./AsyncUtils');
// 校验构造函数是否通过new关键字调用
function _classCallCheck(instance,Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
// 添加或移除延迟执行的钩子函数,类似jquery的callback模块
var PendingHooks = function PendingHooks() {
var _this = this;
_classCallCheck(this,PendingHooks);
this.hooks = [];
this.add = function (hook) {
return _this.hooks.push(hook);
};
this.remove = function (hook) {
return _this.hooks = _this.hooks.filter(function (h) {
return h !== hook;
});
};
this.has = function (hook) {
return _this.hooks.indexOf(hook) !== -1;
};
this.clear = function () {
return _this.hooks = [];
};
};
var enterHooks = new PendingHooks();
var changeHooks = new PendingHooks();
function createTransitionHook(hook,asyncArity,pendingHooks) {
var isSync = hook.length < asyncArity;// hook为函数,asyncArity为真值时,isSync为真值
var transitionHook = function transitionHook() {
for (var _len = arguments.length,args = Array(_len),_key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
hook.apply(route,args);
if (isSync) {
var callback = args[args.length - 1];
callback();
}
};
pendingHooks.add(transitionHook);
return transitionHook;
}
function getEnterHooks(routes) {
return routes.reduce(function (hooks,route) {
if (route.onEnter) hooks.push(createTransitionHook(route.onEnter,3,enterHooks));
return hooks;
},[]);
}
// routes是当前路径下被激活的Route组件,
function getChangeHooks(routes) {
return routes.reduce(function (hooks,route) {
if (route.onChange) hooks.push(createTransitionHook(route.onChange,4,changeHooks));
return hooks;
},[]);
}
function runTransitionHooks(length,iter,callback) {
if (!length) {
callback();
return;
}
// Redirect元素处于激活状态,先通过Redirect元素的props.to重定向页面,再根据页面路径获取加载的组件
var redirectInfo = void 0;
function replace(location) {
redirectInfo = location;
}
(0,_AsyncUtils.loopAsync)(length,done) {
iter(index,replace,function (error) {
if (error || redirectInfo) {
done(error,redirectInfo);
} else {
next();
}
});
},callback);
}
// 路径改变时执行route.onEnter方法,异步在callback回调中处理
function runEnterHooks(routes,callback) {
enterHooks.clear();
var hooks = getEnterHooks(routes);
return runTransitionHooks(hooks.length,next) {
var wrappedNext = function wrappedNext() {
if (enterHooks.has(hooks[index])) {
next();
enterHooks.remove(hooks[index]);
}
};
// nextState,replace作为route.onChange方法,wrappedNext为hooks的回调
hooks[index](nextState,wrappedNext);
},callback);
}
// 路径改变时执行route.onChange方法,异步在callback回调中处理
function runChangeHooks(routes,callback) {
// route.onChange方法为什么要用createTransitionHook函数进行封装,调用完成后又清空该transitionHook???
changeHooks.clear();
var hooks = getChangeHooks(routes);
return runTransitionHooks(hooks.length,next) {
var wrappedNext = function wrappedNext() {
if (changeHooks.has(hooks[index])) {
next();
changeHooks.remove(hooks[index]);
}
};
// state,replace作为route.onChange方法,wrappedNext为hooks的回调
hooks[index](state,callback);
}
// 离开Route时调用route.onLeave方法
function runLeaveHooks(routes,prevState) {
for (var i = 0,len = routes.length; i < len; ++i) {
if (routes[i].onLeave) routes[i].onLeave.call(routes[i],prevState);
}
}