react-router源码

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,routes,params,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,b,c,d,e,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,params,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,location,routes,params,components,createElement,props})
                    // props含有部分Router组件接收到的props
        createElement: func,// 负责Route下待挂载组件的渲染createElement(component,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,params,location,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,routes,params,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,routes,params,components},并引起RouterContent组件重绘。传入首参location的实质性意义是通过待变更路径数据获取路径数据变量params、即将激活的routes元素(包括Route、IndexRoute、Redirect、IndexRedirect元素),由routes元素获取待挂载的组件components,最终获得待更待state={location,routes,params,components}数据或者Redirect、IndexRedirect元素激活时的重定向地址redirectLocation,实现页面跳转或更新Router元素的state。

listen(listener) 该方法只在react-route内部使用,即Router.js模块中调用,意义是监听hashChange、popstate事件,通过当前页面路径数据获取更迭后的state={location,routes,params,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,routes,params)判断路径数据_ref同当前路径是否相同
// 参数_ref待校验的路径数据location
// indexOnly为真值时路径pathname需要完全匹配,否则pathname匹配激活的route设下的路由规则即可
// 参数currentLocation,routes,params当前页面相关数据
var _isActive2 = require('./isActive');
var _isActive3 = _interopRequireDefault(_isActive2);

// getComponents(nextState,callback)获取激活路径下挂载的多个组件,并执行回调
var _getComponents = require('./getComponents');
var _getComponents2 = _interopRequireDefault(_getComponents);

// matchRoutes(routes,location,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,indexOnly,currentLocation,routes,params)判断路径数据_ref同当前路径是否相同
        // 参数_ref待校验的路径数据location
        // indexOnly为真值时路径pathname需要完全匹配,否则pathname匹配激活的route设下的路由规则即可
        // 参数currentLocation,routes,params当前页面相关数据
        return (0, _isActive3.default)(location, indexOnly, state.location, state.routes, state.params);
    }

    var partialNextState = void 0;

    // 参数location变更后的路径数据
    // 获取激活的Route信息、待挂载的组件信息、路径变量信息(即state数据信息)
    // state以{routes,params,location,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,location,callback)获取当前路径下被激活的Route组件以及路径变量信息params,{routes,params}形式
            // 并且将{routes,params}传入回调callback函数作为参数
            (0, _matchRoutes2.default)(routes, location, function (error, nextState) {
                if (error) {
                    callback(error);
                } else if (nextState) {
                    finishMatch(_extends({}, nextState, { location: location }), callback);
                } else {
                    callback();
                }
            });
        }
    }

    // 参数nextState由match函数传入,{routes,params,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, nextState, function (error, redirectInfo) {
            if (error || redirectInfo) return handleErrorOrRedirect(error, redirectInfo);

            // 间接执行Route及IndexRoute及Redirect组件上用户设置的onEnter方法
            // Redirect元素被激活时,将触发该元素的onEnter方法,重定向页面路径
            (0, _TransitionUtils.runEnterHooks)(enterRoutes, nextState, finishEnterHooks);
        });

        function finishEnterHooks(error, redirectInfo) {
            if (error || redirectInfo) return handleErrorOrRedirect(error, redirectInfo);
            
            // getComponents(nextState,callback)获取激活路径下挂载的多个组件,并执行回调
            (0, _getComponents2.default)(nextState, function (error, components) {
                if (error) {
                    callback(error);
                } else {
                    // callback执行参数state为{routes,params,location}形式
                    callback(null, null, state = _extends({}, nextState, { 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,location,callback)获取当前路径下被激活的Route组件以及路径变量信息params,{routes,params}形式
        // 并且将{routes,params}传入回调callback函数作为参数nextState
        (0, _matchRoutes2.default)(routes, location, function (error, nextState) {
            if (nextState == null) {
                callback();
                return;
            }

            partialNextState = _extends({}, nextState, { location: location });

            // 获取用户调用transitionManager.listenBeforeLeavingRoute(hook)添加的前置钩子执行函数
            // _computeChangedRoutes3.default(prevState,nextState)计算确切的changeRoutes、leaveRoutes、enterRoutes
            var hooks = getRouteHooksForRoutes((0, _computeChangedRoutes3.default)(state, 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,params,location,component}形式,执行回调
                // 期间触发Route组件配置的route.onChange|onEnter|onLeave方法执行
                match(location, function (error, 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,params,location,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,location,params,routes},元素由对应的路径数据、路径变量、激活的route元素完成实例化。

 Router.js模块提供createRouterObject、assignRouterState两个方法。createRouterObject用于创建this.router对象,assignRouterState用于路径变更,引起Router组件setState方法执行前,更新this.router的location、params、routes属性指向待变更路径数据的对应属性。

this.router的属性和方法:

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) 跳转页面,将触发前置钩子和后置钩子的执行。

go、goBack、goForward 通过window.history.go跳转页面。

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,transitionManager,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, transitionManager, 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,location,params,routes,components,createElement}借由Router.js监听hashChange、popstate事件触发setState方法执行、完成更迭。

当通过<Route path="message/:id" component={Component}/>挂载Component元素时,该Component元素的props形式为{location,params,routes,router,route,routeParams},其中location,params,routes为当前页面对应的路径数据、路径变量、激活的route元素(如Route、IndexRoute、Redirect、IndexRedirect元素);router为Router元素的this.router,用于操作页面跳转、监听路径跳转、获取当前的路径数据等;route为该Component元素相应的Route或IndexRoute元素;routeParams为与该Component元素对应的Route,其下设的路径变量数据,如{id:1}形式。

特别的,当通过<Route path="/" component={App}> <Route path="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,format,a,b,c,d,e,f),condition为否值时,以a,b,c,d,e,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,
    func = _React$PropTypes.func,
    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,
            routes = _props.routes,
            params = _props.params,
            components = _props.components,
            router = _props.router;

        var element = null;

        if (components) {
            // 由底层向上创建待挂载渲染的route.props.component|components组件元素  
            element = components.reduceRight(function (element, components, 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,
                    params: params,
                    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, _invariant2.default)(false, '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}> < IndexRoute component={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,nextState,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,format,a,b,c,d,e,f),condition为否值时,以a,b,c,d,e,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,nextState,replace),// 路径改变时调用函数
//     onEnter(nextState,replace),// 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,nextState,replace) 路径改变时调用函数
        //onEnter(nextState,replace) route激活时调用函数
    },

    // 不负责渲染
    render: function render() {
        !false ? process.env.NODE_ENV !== 'production' ? 
            (0, _invariant2.default)(false, 
                '<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,format,a,b,c,d,e,f),condition为否值时,以a,b,c,d,e,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, _routerWarning2.default)(false, 
                          '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, _invariant2.default)(false, 
                '<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,format,a,b,c,d,e,f),condition为否值时,以a,b,c,d,e,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,
    string = _React$PropTypes.string,
    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;
        }
    },

    propTypes: {
        path: string,
        from: string, // Alias for path
        to: string.isRequired,
        query: object,
        state: object,
        onEnter: _InternalPropTypes.falsy,
        children: _InternalPropTypes.falsy
    },

    render: function render() {
        !false ? process.env.NODE_ENV !== 'production' ? 
            (0, _invariant2.default)(false, 
                '<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,format,a,b,c,d,e,f),condition为否值时,以a,b,c,d,e,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,
    string = _React$PropTypes.string,
    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, _routerWarning2.default)(false, 
                        'An <IndexRedirect> does not make sense at the root of your route config') 
                    : void 0;
            }
        }
    },

    propTypes: {
        to: string.isRequired,
        query: object,
        state: object,
        onEnter: _InternalPropTypes.falsy,
        children: _InternalPropTypes.falsy
    },

    render: function render() {
        !false ? process.env.NODE_ENV !== 'production' ? 
            (0, _invariant2.default)(false, 
                  '<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,format,a,b,c,d,e,f),condition为否值时,以a,b,c,d,e,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, 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,
    bool = _React$PropTypes.bool,
    object = _React$PropTypes.object,
    string = _React$PropTypes.string,
    func = _React$PropTypes.func,
    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添加时待跳转页面是否需要严格匹配路径
//     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]),
        query: object,
        hash: string,
        state: object,
        activeStyle: object,
        activeClassName: string,
        onlyActiveOnIndex: bool.isRequired,
        onClick: func,
        target: string
    },

    getDefaultProps: function getDefaultProps() {
        return {
            onlyActiveOnIndex: false,
            style: {}
        };
    },

    // 点击时通过router.push跳转页面,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, _invariant2.default)(false, '<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({}, props, { 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, _extends({}, 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,format,a,b,c,d,e,f),condition为否值时,以a,b,c,d,e,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, _ContextUtils.ContextSubscriber)('router')],

        contextTypes: { router: _PropTypes.routerShape },
        propTypes: { router: _PropTypes.routerShape },

        getWrappedInstance: function getWrappedInstance() {
            !withRef ? process.env.NODE_ENV !== 'production' ? (0, _invariant2.default)(false, '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({}, this.props, { router: router, params: params, location: location, 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对象,用于跳转页面,监听hashChange、popstate事件,获取当前页面路径数据等。

hashHistory.js模块由histroy模块创建的hashHistory对象,用于跳转页面,监听hashChange、popstate事件,获取当前页面路径数据等。

createMemoryHistory.js模块调用histroy模块的createMemoryHistory,用于创建memoryHistory对象,实现跳转页面,监听hashChange、popstate事件,获取当前页面路径数据等。

各history对象作为Router元素的props.history,使Router实现监听hashChange、popstate事件并更新state、重绘组件,以及this.router获得跳转页面、添加前置钩子等方法。

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,state,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跳转页面
//     goBack,// window.history.go跳转页面
//     goForward,// window.history.go跳转页面
//     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,// getUserConfirmation(message,callback) 在callback回调中定制执行window.confirm(message)
//     forceRefresh,// 默认值false,即不使用window.location.href(path)或window.location.replace(path)更新页面路径
//     getCurrentLocation,// 获取当前location数据{pathname,search,hash,state,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跳转页面
//     goBack,// window.history.go跳转页面
//     goForward,// window.history.go跳转页面
//     createKey,// 随机生成一个key,作为查询字符串中的值,以及window.sessionStorage中的键
//     createPath,// 生成路径
//     createHref,// 生成路径
//     createLocation,// 生成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,// getUserConfirmation(message,callback) 在callback回调中定制执行window.confirm(message)
//     queryKey,// 哈希路径中查询字符串的键,值为window.sessionStorage存储state的键
//     hashType,// 规定哈希路径hashPath书写形式
//     getCurrentLocation,// 获取当前location数据{pathname,search,hash,state,action,key}
//     pushLocation,// 哈希路径重新赋值,并使用window.sessionStorage存储{[key]:state}形式的location哈希路径数据
//     replaceLocation,// 哈希路径重新赋值,并使用window.sessionStorage存储{[key]:state}形式的location哈希路径数据
//     listenBefore,// 将transitionTo方法绑定到hashChange事件上,添加前置钩子;事件触发时,执行钩子,及变更浏览器location及window.sessionStorage存储
//     listen,// 将transitionTo方法绑定到hashChange事件上,添加后置钩子;事件触发时,执行钩子,及变更浏览器location及window.sessionStorage存储
//     transitionTo,// 变更页面路径、更新window.sessionStorage、执行钩子函数,更新allKeys,可作为页面路径变更时的回调
//     push,// transitionTo方法变更页面路径、更新window.sessionStorage、执行钩子函数,allKeys添加一条路径记录,可作为页面路径变更时的回调
//     replace,// transitionTo方法变更页面路径、更新window.sessionStorage、执行钩子函数,allKeys替换一条路径记录,可作为页面路径变更时的回调
//     go,// window.history.go跳转页面
//     goBack,// window.history.go跳转页面
//     goForward,// window.history.go跳转页面
//     createKey,// 随机生成一个key,作为查询字符串中的值,以及window.sessionStorage中的键
//     createPath,// 生成路径
//     createHref,// 生成哈希路径
//     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,// getUserConfirmation(message,callback) 在callback回调中定制执行window.confirm(message)
//     queryKey,// 默认值'_k',哈希路径中查询字符串的键,值为window.sessionStorage存储state的键
//     hashType,// 默认值"slash",规定哈希路径hashPath书写形式
//     getCurrentLocation,// 获取当前location数据{pathname,search,hash,state,action,key}
//     pushLocation,// 哈希路径重新赋值,并使用window.sessionStorage存储{[key]:state}形式的location哈希路径数据
//     replaceLocation,// 哈希路径重新赋值,并使用window.sessionStorage存储{[key]:state}形式的location哈希路径数据
//     listenBefore,// 将transitionTo方法绑定到hashChange事件上,添加前置钩子;事件触发时,执行钩子,及变更浏览器location及window.sessionStorage存储
//     listen,// 将transitionTo方法绑定到hashChange事件上,添加后置钩子;事件触发时,执行钩子,及变更浏览器location及window.sessionStorage存储
//     transitionTo,// 变更页面路径、更新window.sessionStorage、执行钩子函数,更新allKeys,可作为页面路径变更时的回调
//     push,// transitionTo方法变更页面路径、更新window.sessionStorage、执行钩子函数,allKeys添加一条路径记录,可作为页面路径变更时的回调
//     replace,// transitionTo方法变更页面路径、更新window.sessionStorage、执行钩子函数,allKeys替换一条路径记录,可作为页面路径变更时的回调
//     go,// window.history.go跳转页面
//     goBack,// window.history.go跳转页面
//     goForward,// window.history.go跳转页面
//     createKey,// 随机生成一个key,作为查询字符串中的值,以及window.sessionStorage中的键
//     createPath,// 生成路径
//     createHref,// 生成哈希路径
//     createLocation,// 生成location数据
// }
exports.default = (0, _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,search,hash,key,state}构成的数组时,将options赋值给options.entries;
//                 options为字符串时,将[options]赋值给options.entries
// options.current 默认取options.entries.length-1
// 返回值
// {    
//     entries,// {path,search,hash,key,state}对象构成的路径数据数组
//     getCurrentLocation,// 获取当前location数据{pathname,search,hash,state,action,key}
//     pushLocation,// entries、storage添加一条路径记录
//     replaceLocation,// entries、storage替换一条路径记录
//     listenBefore,// 添加后置钩子
//     listen,// 添加后置钩子
//     transitionTo,// 变更页面路径、更新storage、执行钩子函数,更新allKeys,可作为页面路径变更时的回调
//     push,// transitionTo方法变更页面路径、更新storage、执行钩子函数,allKeys添加一条路径记录,可作为页面路径变更时的回调
//     replace,// transitionTo方法变更页面路径、更新storage、执行钩子函数,allKeys替换一条路径记录,可作为页面路径变更时的回调
//     go,// 变更页面路径、更新storage、执行钩子函数,更新allKeys
//     goBack,// window.history.go跳转页面
//     goForward,// window.history.go跳转页面
//     createKey,// 随机生成一个key,作为查询字符串中的值,以及window.sessionStorage中的键
//     createPath,// 生成路径
//     createHref,// 生成哈希路径
//     createLocation,// 生成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,routes,location}, function(error,redirectLocation,renderProps) )方法。获取重定向路径数据redirectLocation、或_ref. location相应的renderProps={outer,matchContext,location,params,routes,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,format,a,b,c,d,e,f),condition为否值时,以a,b,c,d,e,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,transitionManager,state)方法
//    创建复合对象,含有histroy绑定路由事件、获取路由数据、跳转路由相关方法
//    以及setRouteLeaveHook、isActive方法
//    及location、params、routes属性(同Router组件当前state的相关属性等值)
// assignRouterState(router,_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, 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; 
};

// 服务器端异步渲染routes使用,match({history,routes,location}, function(error,redirectLocation,renderProps))
// 通过_ref.history、_ref.routes创建transitionManager对象,并执行transitionManager.match方法
// transitionManager.match方法中,获取_ref.location引起重定向路径数据redirectLocation、更迭的state数据nextState
//    通过nextState={location,params,routes,components}获得renderProps={router,matchContext,location,params,routes,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, _invariant2.default)(false, '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, (0, _RouteUtils.createRoutes)(routes));

    if (location) {
        location = history.createLocation(location);
    } else {
        location = history.getCurrentLocation();
    }

    transitionManager.match(location, function (error, redirectLocation, nextState) {
        var renderProps = void 0;

        if (nextState) {
            var router = (0, _RouterUtils.createRouterObject)(history, transitionManager, nextState);
            renderProps = _extends({}, nextState, {
                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 (previous, renderRouteComponent) {
                return renderRouteComponent(previous, props);
            }, 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 (previous, renderRouterContext) {
            return renderRouterContext(previous, renderProps);
        }, _react2.default.createElement(_RouterContext2.default, _extends({}, 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, _useQueries2.default)((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++,next,done)迭代执行函数,callback回调

mapAsync(array,work,callback)函数以work函数遍历执行array的数组项item,work的第三个参数函数为每次遍历时执行的回调;该回调用以获取callback的参数values,并判断遍历完成是否完成,完成后执行callback

'use strict';

exports.__esModule = true;

// loopAsync(turns,work,callback)函数设定以高阶函数work(currentTurn,next,done)操作长度为turns的数组(纯数据或函数集)
//    turns迭代最大次数,work(currentTurn++,next,done)迭代执行函数,callback回调
// mapAsync(array,work,callback)函数以work函数遍历执行array的数组项item,work的第三个参数函数为每次遍历时执行的回调
//    该回调用以获取callback的参数values,并判断遍历完成是否完成,完成后执行callback
var _AsyncUtils = require('./AsyncUtils');

// isPromise(obj)判断obj是否promise对象
var _PromiseUtils = require('./PromiseUtils');

// 获取激活Route下挂载的组件,并执行回调
function getComponentsForRoute(nextState, route, 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, nextState, 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, route, 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, prevState, 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, key)) { 
                target[key] = source[key]; 
            } 
        } 
    } 

    return target; 
};

exports.default = withRouter;

// node模块;invariant(condition,format,a,b,c,d,e,f),condition为否值时,以a,b,c,d,e,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, _ContextUtils.ContextSubscriber)('router')],

        contextTypes: { router: _PropTypes.routerShape },
        propTypes: { router: _PropTypes.routerShape },

        getWrappedInstance: function getWrappedInstance() {
            !withRef ? process.env.NODE_ENV !== 'production' ? (0, _invariant2.default)(false, '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({}, this.props, { router: router, params: params, location: location, 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'];

 

getComponents.js

getComponents.js提供getComponent方法。

getComponents(nextState,callback)获取激活路径下挂载的多个组件,并执行回调。

'use strict';

exports.__esModule = true;

// loopAsync(turns,work,callback)函数设定以高阶函数work(currentTurn,next,done)操作长度为turns的数组(纯数据或函数集)
//    turns迭代最大次数,work(currentTurn++,next,done)迭代执行函数,callback回调
// mapAsync(array,work,callback)函数以work函数遍历执行array的数组项item,work的第三个参数函数为每次遍历时执行的回调
//    该回调用以获取callback的参数values,并判断遍历完成是否完成,完成后执行callback
var _AsyncUtils = require('./AsyncUtils');

// isPromise(obj)判断obj是否promise对象
var _PromiseUtils = require('./PromiseUtils');

// 获取激活Route下挂载的组件,并执行回调
function getComponentsForRoute(nextState, route, 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, nextState, 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, route, callback);
    }, 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,
    object = _react.PropTypes.object,
    shape = _react.PropTypes.shape,
    string = _react.PropTypes.string;
var routerShape = exports.routerShape = shape({
    push: func.isRequired,
    replace: func.isRequired,
    go: func.isRequired,
    goBack: func.isRequired,
    goForward: func.isRequired,
    setRouteLeaveHook: func.isRequired,
    isActive: func.isRequired
});

var locationShape = exports.locationShape = shape({
    pathname: string.isRequired,
    search: string.isRequired,
    state: object,
    action: string.isRequired,
    key: string
});

 

 

isActive.js

isActive.js模块提供isActive方法。

isActive(_ref, indexOnly, currentLocation, routes, 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, routes, 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,routes,params当前页面相关数据
function isActive(_ref, indexOnly, currentLocation, routes, 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, routes, params)) {
            return false;
        }
    }

    return queryIsActive(query, currentLocation.query);
}
module.exports = exports['default'];

 

matchRoutes.js

matchRoutes.js提供matchRoutes方法。

matchRoutes(routes, location, 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,work,callback)函数设定以高阶函数work(currentTurn,next,done)操作长度为turns的数组(纯数据或函数集)
//    turns迭代最大次数,work(currentTurn++,next,done)迭代执行函数,callback回调
// mapAsync(array,work,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, location, 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, function (error, 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, (0, _RouteUtils.createRoutes)(childRoutes));
    }, callback);

    sync = false;
    return result; // Might be undefined.
}

// IndexRoute处于路径激活状态,肯定是最末一个激活Route的子组件
// 激活的Route中添加相应的IndexRoute,意义还是获取待加载的组件
function getIndexRoute(route, location, paramNames, paramValues, callback) {
    if (route.indexRoute) {
        callback(null, route.indexRoute);
    } else if (route.getIndexRoute) {// 父Route以props配置书写getIndexRoute函数获取IndexRoute,供回调使用
        var partialNextState = {
            location: location,
            params: createParams(paramNames, paramValues)
        };

        var indexRoutesReturn = route.getIndexRoute(partialNextState, function (error, 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, (0, _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, next, done) {
                getIndexRoute(pathless[index], location, paramNames, paramValues, function (error, 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, paramNames, 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({}, paramNames, paramValues);
}

// 参数route是逐层遍历Route组件时该组件的配置项
//     Route组件遍历通过反复调用matchRoutes函数实现,每次调用将遍历平级的Route组件
// 参数location当前页面路径数据
// 参数remainingPathname用于递归调用matchRouteDeep记录未被正则匹配的路径
// 参数paramNames、paramValues用于递归调用matchRouteDeep记录已获取的路径变量键及其值
// callback用于区别执行_AsyncUtils.loopAsync函数机制的done、next迭代函数
function matchRouteDeep(route, location, remainingPathname, paramNames, paramValues, 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, _PatternUtils.matchPattern)(pattern, remainingPathname);
            if (matched) {
                remainingPathname = matched.remainingPathname;
                paramNames = [].concat(paramNames, matched.paramNames);
                paramValues = [].concat(paramValues, matched.paramValues);
            } else {
                remainingPathname = null;
            }
        } catch (error) {
            callback(error);
        }

        // 当前页面路径location数据已经被遍历的route匹配完毕,即当前路径下所有激活的Route
        // IndexRoute处于路径激活状态,肯定是最末一个激活Route的子组件
        if (remainingPathname === '') {
            var _ret2 = function () {
                var match = {
                    routes: [route],
                    params: createParams(paramNames, paramValues)
                };

                // 激活的Route中添加相应的IndexRoute,意义还是获取待加载的组件
                getIndexRoute(route, location, paramNames, paramValues, function (error, 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, location, function (error, 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();
                    }
                }, remainingPathname, paramNames, paramValues);
            } else {
                callback();
            }
        };

        // getChildRoutes函数同步或者异步方式获取childRoutes,通过route.childRoutes属性或route.getChildRoutes方法
        var result = getChildRoutes(route, location, paramNames, paramValues, onChildRoutes);
        if (result) {
            onChildRoutes.apply(undefined, result);
        }
    } else {
        callback();
    }
}

// matchRoutes(routes,location,callback)外部调用,
// 获取当前路径下被激活的Route组件以及路径变量信息params,{routes,params}形式
// 并且将{routes,params}传入回调callback函数作为参数
// matchRoutes(routes,location,callback,remainingPathname)内部调用,遍历子Route组件,获取激活子Route信息
function matchRoutes(routes, location, callback, 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({}, location, {
                pathname: '/' + location.pathname
            });
        }
        remainingPathname = location.pathname;
    }

    // _AsyncUtils.loopAsync(turns,work,callback)函数设定以高阶函数work(currentTurn,next,done)操作长度为turns的数组(纯数据或函数集)
    //    turns迭代最大次数,work(currentTurn++,next,done)迭代执行函数,callback回调
    (0, _AsyncUtils.loopAsync)(routes.length, function (index, next, done) {
        matchRouteDeep(routes[index], location, remainingPathname, paramNames, paramValues, function (error, 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,paramNames,tokens}正则匹配规则、路径变量、原始路径、拆分路径等。

matchPattern(pattern, pathname) 获取pathname下未符合路由规则的remainingPathname,数组形式的路径变量键与值,返回值为{remainingPathname,paramNames,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,format,a,b,c,d,e,f),condition为否值时,以a,b,c,d,e,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, _invariant2.default)(false, '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, _invariant2.default)(false, 'Missing "%s" parameter for path "%s"', paramName, pattern) : 
                (0, _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, _invariant2.default)(false, '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, _invariant2.default)(false, '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';
}

 

 routerWarning.js 错误提示

'use strict';

exports.__esModule = true;
exports.default = routerWarning;
exports._resetWarned = _resetWarned;

// node模块;warning(condition,format,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, transitionManager, state)创建router对象,用于跳转页面、监听事件、获取当前页面路径数据等。

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, key)) { 
                target[key] = source[key]; 
            } 
        } 
    } 

    return target; 
};

// createRouterObject(history,transitionManager,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, transitionManager, 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;
}

 

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,work,callback)函数设定以高阶函数work(currentTurn,next,done)操作长度为turns的数组(纯数据或函数集)
//    turns迭代最大次数,work(currentTurn++,next,done)迭代执行函数,callback回调
// mapAsync(array,work,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, route, 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, route, 3, enterHooks));
        return hooks;
    }, []);
}

// routes是当前路径下被激活的Route组件,
function getChangeHooks(routes) {
    return routes.reduce(function (hooks, route) {
        if (route.onChange) hooks.push(createTransitionHook(route.onChange, route, 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, function (index, next, done) {
        iter(index, replace, function (error) {
            if (error || redirectInfo) {
                done(error, redirectInfo);
            } else {
                next();
            }
        });
    }, callback);
}

// 路径改变时执行route.onEnter方法,异步在callback回调中处理
function runEnterHooks(routes, nextState, callback) {
    enterHooks.clear();
    var hooks = getEnterHooks(routes);
    return runTransitionHooks(hooks.length, function (index, replace, next) {
        var wrappedNext = function wrappedNext() {
            if (enterHooks.has(hooks[index])) {
                next();
                enterHooks.remove(hooks[index]);
            }
        };
        // nextState,replace作为route.onChange方法,wrappedNext为hooks的回调
        hooks[index](nextState, replace, wrappedNext);
    }, callback);
}

// 路径改变时执行route.onChange方法,异步在callback回调中处理
function runChangeHooks(routes, state, nextState, callback) {
    // route.onChange方法为什么要用createTransitionHook函数进行封装,调用完成后又清空该transitionHook???
    changeHooks.clear();
    var hooks = getChangeHooks(routes);
    return runTransitionHooks(hooks.length, function (index, replace, next) {
        var wrappedNext = function wrappedNext() {
            if (changeHooks.has(hooks[index])) {
                next();
                changeHooks.remove(hooks[index]);
            }
        };
        // state,nextState,replace作为route.onChange方法,wrappedNext为hooks的回调
        hooks[index](state, nextState, replace, wrappedNext);
    }, 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);
    }
}

 

猜你喜欢

转载自schifred.iteye.com/blog/2352906