history模块源码

node模块warning.js

'use strict';

var __DEV__ = process.env.NODE_ENV !== 'production';

var warning = function() {};

if (__DEV__) {
    // warning(condition,format,args) 开发环境下,condition为否值时,使用args替换报错字符串模板format输出
    warning = function(condition, format, args) {
        var len = arguments.length;
        args = new Array(len > 2 ? len - 2 : 0);
        for (var key = 2; key < len; key++) {
            args[key - 2] = arguments[key];
        }
        if (format === undefined) {
            throw new Error(
                '`warning(condition, format, ...args)` requires a warning ' +
                'message argument'
            );
        }

        // 报错字符串模板format内容不充分
        if (format.length < 10 || (/^[s\W]*$/).test(format)) {
            throw new Error(
                'The warning format should be able to uniquely identify this ' +
                'warning. Please, use a more descriptive format than: ' + format
            );
        }

        if (!condition) {
            var argIndex = 0;
            var message = 'Warning: ' +
                    format.replace(/%s/g, function() {
                        return args[argIndex++];
                    });

            if (typeof console !== 'undefined') {
                console.error(message);
            }

            try {
                throw new Error(message);
            } catch(x) {}
        }
    };
}

module.exports = warning;

node模块invariant.js

'use strict';

var NODE_ENV = process.env.NODE_ENV;

// invariant(condition,format,a,b,c,d,e,f),condition为否值时,以a,b,c,d,e,f替换报错模板字符串format,抛出错误
var invariant = function(condition, format, a, b, c, d, e, f) {
    // 开发环境下模板字符串format缺省,报错
    if (NODE_ENV !== 'production') {
        if (format === undefined) {
            throw new Error('invariant requires an error message argument');
        }
    }

    if (!condition) {
        var error;

        // 生产环境下模板字符串format缺省,报错
        if (format === undefined) {
            error = new Error(
                'Minified exception occurred; use the non-minified dev environment ' +
                'for the full error message and additional helpful warnings.'
            );
        } else {
            var args = [a, b, c, d, e, f];
            var argIndex = 0;
            error = new Error(
                format.replace(/%s/g, function() { return args[argIndex++]; })
            );
            error.name = 'Invariant Violation';
        }

        error.framesToPop = 1; 
        throw error;
    }
};

module.exports = invariant;

ExecutionEnvironment.js侦测是否浏览器环境,可操作dom节点

DomUtils.js绑定事件、浏览器能力检测

runTransitionHook.js执行钩子函数

AsyncUtils.js异步流程处理

Action.js约定location.action可赋予的值,location.action影响allKeys跳转路径记录

PathUtils.js路径操作相关工具函数

LocationUtils.js通过路径和存储state数据创建location路径数据对象,比较两个路径数据对象是否相同

/*---------- ExecutionEnvironment.js模块 -----------*/
'use strict';

// 判断平台是否为浏览器环境,可以操作dom节点
exports.__esModule = true;
var canUseDOM = exports.canUseDOM = 
	!!(typeof window !== 'undefined' && window.document && window.document.createElement);



/*---------- DomUtils.js模块 -----------*/
'use strict';

exports.__esModule = true;

// dom节点绑点、移除函数方法封装
var addEventListener = exports.addEventListener = function addEventListener(node, event, listener) {
  	return node.addEventListener ? 
  		node.addEventListener(event, listener, false) : node.attachEvent('on' + event, listener);
};

var removeEventListener = exports.removeEventListener = function removeEventListener(node, event, listener) {
  	return node.removeEventListener ? 
  		node.removeEventListener(event, listener, false) : node.detachEvent('on' + event, listener);
};

// 能力检测,判断浏览器是否支持HTML5 history API
var supportsHistory = exports.supportsHistory = function supportsHistory() {
	var ua = window.navigator.userAgent;

	if ((ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) 
			&& ua.indexOf('Mobile Safari') !== -1 && ua.indexOf('Chrome') === -1 
			&& ua.indexOf('Windows Phone') === -1) return false;

	return window.history && 'pushState' in window.history;
};

// 能力检测,判断window.history.go(n)方法更新哈希路径是否会导致页面重新加载
var supportsGoWithoutReloadUsingHash = exports.supportsGoWithoutReloadUsingHash = function supportsGoWithoutReloadUsingHash() {
  	return window.navigator.userAgent.indexOf('Firefox') === -1;
};

// 能力检测,判断哈希路径改变时是否会触发popstate事件
var supportsPopstateOnHashchange = exports.supportsPopstateOnHashchange = function supportsPopstateOnHashchange() {
  	return window.navigator.userAgent.indexOf('Trident') === -1;
};



/*---------- runTransitionHook.js模块 -----------*/
'use strict';

exports.__esModule = true;

// 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 }; }

// 执行hook(location),callback为hook函数执行完成后的回调
// 执行hook(location,callback),callback在hook函数内部调用,hook不能返回真值
var runTransitionHook = function runTransitionHook(hook, location, callback) {
	var result = hook(location, callback);

	// hook为函数时,hook.length为函数声明时传参的个数
	if (hook.length < 2) {
	    callback(result);
	} else {
		// hook接受两个参数,即callback在hook中调用,result不能为真值
    	process.env.NODE_ENV !== 'production' ? 
    		(0, _warning2.default)(result === undefined, 
    			'You should not "return" in a transition hook with a callback argument; ' 
    			+ 'call the callback instead') 
    		: void 0;
	}
};

exports.default = runTransitionHook;



/*---------- AsyncUtils.js模块 -----------*/
"use strict";

exports.__esModule = true;

// 设定以高阶函数work(currentTurn,next,done)操作长度为turns的数组(纯数据或函数集)
// turns迭代最大次数,work(currentTurn++,next,done)迭代执行函数,callback回调
var loopAsync = exports.loopAsync = function loopAsync(turns, work, callback) {
    var currentTurn = 0,
        isDone = false;// 结束迭代标识
    var isSync = false,// 异步的work执行过程中标识,使用next函数参数执行work
        hasNext = false,
        doneArgs = void 0;

    // 主动调用done函数结束迭代,done函数不在延时后调用,执行callback回调
    var done = function done() {
        // done函数接受的参数,作为callback回调的参数
        for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
            args[_key] = arguments[_key];
        }

        isDone = true;

        // done函数同步调用,isSync仍为true,callback回调在next函数中执行,参数为doneArgs
        if (isSync) {
            doneArgs = args;
            return;
        }

        // 执行callback回调
        callback.apply(undefined, args);
    };

    var next = function next() {
        if (isDone) return;

        hasNext = true;

        if (isSync) return;// 异步work执行过程中,跳出
        isSync = true;

        // while循环除作为条件判断以外,没有实质性意义
        while (!isDone && currentTurn < turns && hasNext) {
            hasNext = false;
            // next同步调用时,hasNext直接被赋值为true,同时isSync仍为true,不进入while循环;
            // next异步延时调用,hasNext仍是false,不进入while循环
            work(currentTurn++, next, done);
        }

        isSync = false;

        // done函数同步调用时,执行callback回调
        if (isDone) {
            callback.apply(undefined, doneArgs);
            return;
        }

        // 所有迭代均完成,执行回调
        if (currentTurn >= turns && hasNext) {
            isDone = true;
            callback();
        }
    };

    next();
};


/*---------- Action.js模块 -----------*/
'use strict';

exports.__esModule = true;

// 作为location.action的值,histroy对象中使用push方法将在allKeys中添加一条路径记录
var PUSH = exports.PUSH = 'PUSH';

// 作为location.action的值,histroy对象中使用replace方法将在allKeys中替换一条路径记录
var REPLACE = exports.REPLACE = 'REPLACE';

// 作为location.action的默认值,用户手动变更页面路径,allKeys不作任何处理
var POP = exports.POP = 'POP';



/*---------- PathUtils.js模块 -----------*/
'use strict';

exports.__esModule = true;
exports.createPath = exports.parsePath = exports.getQueryStringValueFromPath 
    = exports.stripQueryStringValueFromPath = exports.addQueryStringValueToPath = undefined;

// 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 }; }

// path下查询字符串search部分添加key+"="+value,重新拼接路径并返回
var addQueryStringValueToPath = exports.addQueryStringValueToPath = function addQueryStringValueToPath(path, key, value) {
    var _parsePath = parsePath(path);

    var pathname = _parsePath.pathname;
    var search = _parsePath.search;
    var hash = _parsePath.hash;

    return createPath({
        pathname: pathname,
        search: search + (search.indexOf('?') === -1 ? '?' : '&') + key + '=' + value,
        hash: hash
    });
};

// path下查询字符串search中含有key键值,将search转化为"?"或"&",构建新的路径字符串
var stripQueryStringValueFromPath = exports.stripQueryStringValueFromPath 
        = function stripQueryStringValueFromPath(path, key) {
            // 将不包含协议名的路径path转化对象形式,对象中含域名path、查询字符串search、哈希路径hash
            var _parsePath2 = parsePath(path);

            var pathname = _parsePath2.pathname;
            var search = _parsePath2.search;
            var hash = _parsePath2.hash;

            return createPath({
                pathname: pathname,
                search: search.replace(new RegExp('([?&])' + key + '=[a-zA-Z0-9]+(&?)'), function (match, prefix, suffix) {
                    return prefix === '?' ? prefix : suffix;
                }),
                hash: hash
            });
        };

// 获取路径path下查询字符串search中,key键的值
var getQueryStringValueFromPath = exports.getQueryStringValueFromPath 
        = function getQueryStringValueFromPath(path, key) {
            // 将不包含协议名的路径path转化对象形式,对象中含域名path、查询字符串search、哈希路径hash
            var _parsePath3 = parsePath(path);

            var search = _parsePath3.search;

            var match = search.match(new RegExp('[?&]' + key + '=([a-zA-Z0-9]+)'));
            return match && match[1];
        };

// 将https://、http://、或//起始的路径转化为空字符串,否则原样输出
var extractPath = function extractPath(string) {
    var match = string.match(/^(https?:)?\/\/[^\/]*/);
    return match == null ? string : string.substring(match[0].length);
};

// 将不包含协议名的路径path转化对象形式,对象中含路径path、查询字符串search、哈希路径hash
var parsePath = exports.parsePath = function parsePath(path) {
    var pathname = extractPath(path);
    var search = '';
    var hash = '';

    // 校验传参path是否以https://、http://、或//起始
    process.env.NODE_ENV !== 'production' ? 
        (0, _warning2.default)(path === pathname, 
            'A path must be pathname + search + hash only, not a full URL like "%s"', path) 
        : void 0;

    // 获取路径中"#"起始的哈希部分hash
    var hashIndex = pathname.indexOf('#');
    if (hashIndex !== -1) {
        hash = pathname.substring(hashIndex);
        pathname = pathname.substring(0, hashIndex);
    }

    // 获取路径中以"?"起始的查询字符串部分search
    // 路径以pathname获取
    var searchIndex = pathname.indexOf('?');
    if (searchIndex !== -1) {
        search = pathname.substring(searchIndex);
        pathname = pathname.substring(0, searchIndex);
    }

    if (pathname === '') pathname = '/';

    return {
        pathname: pathname,
        search: search,
        hash: hash
    };
};

// 将对象形式的路径拼接成路径字符串后返回
var createPath = exports.createPath = function createPath(location) {
    if (location == null || typeof location === 'string') return location;

    var basename = location.basename;
    var pathname = location.pathname;
    var search = location.search;
    var hash = location.hash;

    var path = (basename || '') + pathname;

    if (search && search !== '?') path += search;

    if (hash) path += hash;

    return path;
};



/*---------- LocationUtils.js模块 -----------*/
'use strict';

exports.__esModule = true;
exports.locationsAreEqual = exports.statesAreEqual = exports.createLocation = exports.createQuery = undefined;

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };

// 类似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);

// node模块;warning(condition,format,args) 开发环境下,condition为否值时,使用args替换报错字符串模板format输出
// _warning2.default引用_warning,即node模块warning
var _warning = require('warning');
var _warning2 = _interopRequireDefault(_warning);

// 路径操作工具函数集
var _PathUtils = require('./PathUtils');

// location.action允许的值
var _Actions = require('./Actions');

// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// 使用props创建对象
var createQuery = exports.createQuery = function createQuery(props) {
    return _extends(Object.create(null), props);
};

// createLocation(obj={pathname,search,hash,state}|path,action=undefined,key)
// 将location数据转化为对象形式输出{pathname,search,hash,state,action,key}
var createLocation = exports.createLocation = function createLocation() {
    var input = arguments.length <= 0 || arguments[0] === undefined ? '/' : arguments[0];
    var action = arguments.length <= 1 || arguments[1] === undefined ? _Actions.POP : arguments[1];
    var key = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2];

    // _PathUtils.parsePath将不包含协议名的路径path转化对象形式,对象中含路径path、查询字符串search、哈希路径hash
    var object = typeof input === 'string' ? (0, _PathUtils.parsePath)(input) : input;

    process.env.NODE_ENV !== 'production' ? 
        (0, _warning2.default)(!object.path, 
            'Location descriptor objects should have a `pathname`, not a `path`.') : void 0;

    var pathname = object.pathname || '/';
    var search = object.search || '';
    var hash = object.hash || '';
    var state = object.state;

    return {
        pathname: pathname,
        search: search,
        hash: hash,
        state: state,
        action: action,
        key: key
    };
};

// 是否date对象
var isDate = function isDate(object) {
    return Object.prototype.toString.call(object) === '[object Date]';
};

// 判断变量a、b内容是否相同
var statesAreEqual = exports.statesAreEqual = function statesAreEqual(a, b) {
    if (a === b) return true;

    var typeofA = typeof a === 'undefined' ? 'undefined' : _typeof(a);
    var typeofB = typeof b === 'undefined' ? 'undefined' : _typeof(b);

    if (typeofA !== typeofB) return false;

    !(typeofA !== 'function') ? process.env.NODE_ENV !== 'production' ? (0, _invariant2.default)(false, 'You must not store functions in location state') : (0, _invariant2.default)(false) : void 0;

    if (typeofA === 'object') {
        !!(isDate(a) && isDate(b)) ? process.env.NODE_ENV !== 'production' ? (0, _invariant2.default)(false, 'You must not store Date objects in location state') : (0, _invariant2.default)(false) : void 0;

        if (!Array.isArray(a)) {
            var keysofA = Object.keys(a);
            var keysofB = Object.keys(b);
            return keysofA.length === keysofB.length && keysofA.every(function (key) {
                return statesAreEqual(a[key], b[key]);
            });
        }

        return Array.isArray(b) && a.length === b.length && a.every(function (item, index) {
            return statesAreEqual(item, b[index]);
        });
    }

    return false;
};

// 判断a、b两个location数据对象是否相同
var locationsAreEqual = exports.locationsAreEqual = function locationsAreEqual(a, b) {
    return a.key === b.key &&
        a.pathname === b.pathname && a.search === b.search && a.hash === b.hash 
        && statesAreEqual(a.state, b.state);
};

DomStateStorage.js使用window.sessionStorage存储{key:state}数据,state可以是后台返回的json数据,key由history生成,通过查询字符串location.search(hashHistory情形通过哈希路径部分获取location.search)读取

'use strict';

exports.__esModule = true;
exports.readState = exports.saveState = undefined;

// 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 QuotaExceededErrors = {
    QuotaExceededError: true,
    QUOTA_EXCEEDED_ERR: true
};

var SecurityErrors = {
    SecurityError: true
};

var KeyPrefix = '@@History/';

var createKey = function createKey(key) {
    return KeyPrefix + key;
};

// 使用window.sessionStorage(当前页面有效)存储location.state数据,或者移除数据
var saveState = exports.saveState = function saveState(key, state) {
    if (!window.sessionStorage) {
        process.env.NODE_ENV !== 'production' ? 
            (0, _warning2.default)(false, '[history] Unable to save state; sessionStorage is not available') 
            : void 0;

        return;
    }

    try {
        if (state == null) {
            window.sessionStorage.removeItem(createKey(key));
        } else {
            window.sessionStorage.setItem(createKey(key), JSON.stringify(state));
        }
    } catch (error) {
        if (SecurityErrors[error.name]) {
            process.env.NODE_ENV !== 'production' ? 
                (0, _warning2.default)(false, 
                    '[history] Unable to save state; sessionStorage is not available due to security settings') 
                : void 0;

            return;
        }

        if (QuotaExceededErrors[error.name] && window.sessionStorage.length === 0) {
            process.env.NODE_ENV !== 'production' 
                ? (0, _warning2.default)(false, 
                    '[history] Unable to save state; sessionStorage is not available in Safari private mode') 
                : void 0;

            return;
        }

        throw error;
    }
};

// 读取window.sessionStorage(当前页面有效)存储的location.state数据
var readState = exports.readState = function readState(key) {
    var json = void 0;
    try {
        json = window.sessionStorage.getItem(createKey(key));
    } catch (error) {
        if (SecurityErrors[error.name]) {
            // Blocking cookies in Chrome/Firefox/Safari throws SecurityError on any
            // attempt to access window.sessionStorage.
            process.env.NODE_ENV !== 'production' ? 
                (0, _warning2.default)(false, 
                    '[history] Unable to read state; sessionStorage is not available due to security settings') 
                : void 0;

            return undefined;
        }
    }

    if (json) {
        try {
            return JSON.parse(json);
        } catch (error) {
        }
    }

    return undefined;
};

history.js创建基本路径跳转、绑定函数、添加回调、获取location数据操作,createHashHistory、createBrowserHistory、createMemoryHistory在此基础上拓展功能

'use strict';

exports.__esModule = true;

// loopAsync(turns,work,callback)设定以高阶函数work(currentTurn,next,done)操作长度为turns的数组(纯数据或函数集)
var _AsyncUtils = require('./AsyncUtils');

// 路径操作工具函数集
var _PathUtils = require('./PathUtils');

// runTransitionHook(hook,location,callback)
// 执行hook(location),callback为hook函数执行完成后的回调
// 执行hook(location,callback),callback在hook函数内部调用,hook不能返回真值
var _runTransitionHook = require('./runTransitionHook');
var _runTransitionHook2 = _interopRequireDefault(_runTransitionHook);

// location.action允许的值
var _Actions = require('./Actions');

// 创建、比较location数据对象方法集
var _LocationUtils = require('./LocationUtils');

// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// 创建histroy对象,createHashHistory、createBroswerHistory中调用
// 创建变更页面路径到更新location数据到执行绑点函数的一般方法
var createHistory = function createHistory() {
    // 首参有效
    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

    // 获取当期的路径数据
    // hashHistroy情形,路径数据为{pathname,search,hash,state,action,key},pathname、search、hash都来自哈希路径
    var getCurrentLocation = options.getCurrentLocation;

    // getUserConfirmation(message,callback) 在callback回调中定制执行window.confirm(message)
    var getUserConfirmation = options.getUserConfirmation;

    // 路径重新赋值
    // hashHistroy情形,哈希路径重新赋值,并使用window.sessionStorage存储{[key]:state}形式的location哈希路径数据
    var pushLocation = options.pushLocation;

    // 路径替换
    // hashHistroy情形,替换哈希路径,并使用window.sessionStorage存储{[key]:state}形式的location哈希路径数据
    var replaceLocation = options.replaceLocation;

    // 内部调用window.history.go处理传参
    var go = options.go;

    var keyLength = options.keyLength;

    // 当前的location数据{pathname,search,hash,state,action,key}
    var currentLocation = void 0;

    // 待更新的location数据{pathname,search,hash,state,action,key}
    var pendingLocation = void 0;

    // 页面路径改变后执行的前置钩子函数
    var beforeListeners = [];

    // 页面路径改变后执行的后置钩子函数
    var listeners = [];

    // 以数组记录各次路径改变,便于go函数计算跳转页面
    var allKeys = [];

    // 当前location数据currentLocation.key在allKeys中的位置值
    var getCurrentIndex = function getCurrentIndex() {
        if (pendingLocation && pendingLocation.action === _Actions.POP) return allKeys.indexOf(pendingLocation.key);

        if (currentLocation) return allKeys.indexOf(currentLocation.key);

        return -1;
    };

    // nextLocation.action为_Actions.PUSH时,allKeys添加nextLocation.key,触发listeners回调
    // nextLocation.action为_Actions.REPLACE时,allKeys替换currentLocation.key为nextLocation.key,触发listeners回调 
    // nextLocation.action为_Actions.POP时,不对allKeys进行任何操作,触发listeners回调
    var updateLocation = function updateLocation(nextLocation) {
        var currentIndex = getCurrentIndex();

        currentLocation = nextLocation;

        if (currentLocation.action === _Actions.PUSH) {
            allKeys = [].concat(allKeys.slice(0, currentIndex + 1), [currentLocation.key]);
        } else if (currentLocation.action === _Actions.REPLACE) {
            allKeys[currentIndex] = currentLocation.key;
        }

        listeners.forEach(function (listener) {
            return listener(currentLocation);
        });
    };

    // beforeListeners回调队列中添加listener,返回函数用于移除beforeListeners中的listener
    var listenBefore = function listenBefore(listener) {
        beforeListeners.push(listener);

        return function () {
            return beforeListeners = beforeListeners.filter(function (item) {
                return item !== listener;
            });
        };
    };

    // listeners回调队列中添加listener,返回函数用于移除listeners中的listener
    var listen = function listen(listener) {
        listeners.push(listener);

        return function () {
            return listeners = listeners.filter(function (item) {
                return item !== listener;
            });
        };
    };

    // 迭代执行beforeListeners[index](location),完成后执行callback回调
    var confirmTransitionTo = function confirmTransitionTo(location, callback) {
        // 使用迭代器方式同步执行beforeListeners中的函数,index为该函数在beforeListeners中的序号
        // 迭代完成,执行callback回调
        // transitionTo函数中调用confirmTransitionTo,callback回调的意义是更新页面location、存储location数据
        (0, _AsyncUtils.loopAsync)(beforeListeners.length, function (index, next, done) {
            // 执行beforeListeners[index](location),返回值为null时通过next函数调用beforeListeners[index+1]
            // result为beforeListeners[index](location)的返回值
            (0, _runTransitionHook2.default)(beforeListeners[index], location, function (result) {
                return result != null ? done(result) : next();
            });
        }, function (message) {
            if (getUserConfirmation && typeof message === 'string') {
                getUserConfirmation(message, function (ok) {
                    return callback(ok !== false);
                });
            } else {
                callback(message !== false);
            }
        });
    };

    // 迭代执行前置钩子beforeListeners,变更浏览器location及window.sessionStorage存储的过程
    // 更新allKeys页面路径修改记录,执行listener后置钩子
    // HashProtocol.startListener(history.transitionTo, pathCoder, queryKey)绑定为hashChange事件发生时调用
    var transitionTo = function transitionTo(nextLocation) {
        // nextLocation哈希路径数据同当前的哈希路径数据currentLocation或待更新的哈希路径数据pendingLocation相同
        // 跳过变更浏览器location及window.sessionStorage存储的过程
        if (currentLocation && (0, _LocationUtils.locationsAreEqual)(currentLocation, nextLocation) 
            || pendingLocation && (0, _LocationUtils.locationsAreEqual)(pendingLocation, nextLocation)) 
            return;

        pendingLocation = nextLocation;

        confirmTransitionTo(nextLocation, function (ok) {
            if (pendingLocation !== nextLocation) return; // Transition was interrupted during confirmation

            pendingLocation = null;

            if (ok) {
                // 待变更的location数据nextLocation和当前location数据currentLocation相同
                // nextLocation.action赋值为_Actions.REPLACE
                if (nextLocation.action === _Actions.PUSH) {
                    var prevPath = (0, _PathUtils.createPath)(currentLocation);
                    var nextPath = (0, _PathUtils.createPath)(nextLocation);

                    if (nextPath === prevPath && 
                        (0, _LocationUtils.statesAreEqual)(currentLocation.state, nextLocation.state)) 
                        nextLocation.action = _Actions.REPLACE;
                }

                // nextLocation.action为_Actions.POP时,只触发listeners回调函数队列执行
                if (nextLocation.action === _Actions.POP) {
                    updateLocation(nextLocation);

                // nextLocation.action为_Actions.PUSH时,allKeys添加nextLocation.key
                // 更新window.location页面路径,window.sessionStorage存储{[key]:state}
                // pushLocation(nextLocation)不返回false时(默认undefined),触发listeners回调
                } else if (nextLocation.action === _Actions.PUSH) {
                    if (pushLocation(nextLocation) !== false) updateLocation(nextLocation);

                // nextLocation.action为_Actions.PUSH时,allKeys替换currentLocation.key为nextLocation.key
                // 更新window.location页面路径,window.sessionStorage存储{[key]:state}
                // pushLocation(nextLocation)不返回false时(默认undefined),触发listeners回调
                } else if (nextLocation.action === _Actions.REPLACE) {
                    if (replaceLocation(nextLocation) !== false) updateLocation(nextLocation);
                }

            // 最末一个beforeListeners返回undefined或false时,使用go函数进行页面跳转
            } else if (currentLocation && nextLocation.action === _Actions.POP) {
                var prevIndex = allKeys.indexOf(currentLocation.key);
                var nextIndex = allKeys.indexOf(nextLocation.key);

                if (prevIndex !== -1 && nextIndex !== -1) go(prevIndex - nextIndex);
            }
        });
    };

    // 迭代执行前置钩子beforeListeners,变更浏览器location及window.sessionStorage存储的过程
    // 更新allKeys添加一条页面路径修改记录,执行listener后置钩子
    var push = function push(input) {
        return transitionTo(createLocation(input, _Actions.PUSH));
    };

    // 迭代执行前置钩子beforeListeners,变更浏览器location及window.sessionStorage存储的过程
    // 更新allKeys替换一条页面路径修改记录,执行listener后置钩子
    var replace = function replace(input) {
        return transitionTo(createLocation(input, _Actions.REPLACE));
    };
  
    // 内部调用window.history.go处理传参
    var goBack = function goBack() {
        return go(-1);
    };
    var goForward = function goForward() {
        return go(1);
    };

    // 随机生成一个key,作为查询字符串中的值,以及window.sessionStorage中的键
    var createKey = function createKey() {
        return Math.random().toString(36).substr(2, keyLength || 6);
    };

    // 生成路径
    var createHref = function createHref(location) {
        return (0, _PathUtils.createPath)(location);
    };

    // createLocation(obj={pathname,search,hash,state}|path,action=undefined,key)
    // 将location数据转化为对象形式输出{pathname,search,hash,state,action,key}
    var createLocation = function createLocation(location, action) {
        var key = arguments.length <= 2 || arguments[2] === undefined ? createKey() : arguments[2];
        return (0, _LocationUtils.createLocation)(location, action, key);
    };

    return {
        getCurrentLocation: getCurrentLocation,// 获取当前location数据{pathname,search,hash,state,action,key}
        listenBefore: listenBefore,// 添加前置钩子
        listen: listen,// 添加后置钩子
        transitionTo: transitionTo,// 变更页面路径、更新window.sessionStorage、执行钩子函数,更新allKeys,可作为页面路径变更时的回调
        push: push,// transitionTo方法变更页面路径、更新window.sessionStorage、执行钩子函数,allKeys添加一条路径记录,可作为页面路径变更时的回调
        replace: replace,// transitionTo方法变更页面路径、更新window.sessionStorage、执行钩子函数,allKeys替换一条路径记录,可作为页面路径变更时的回调
        go: go,// window.history.go跳转页面
        goBack: goBack,// window.history.go跳转页面
        goForward: goForward,// window.history.go跳转页面
        createKey: createKey,// 随机生成一个key,作为查询字符串中的值,以及window.sessionStorage中的键
        createPath: _PathUtils.createPath,// 生成路径
        createHref: createHref,// 生成路径
        createLocation: createLocation// 生成location数据
    };
};

exports.default = createHistory;

HashProtocol.js获取当前的哈希路径数据、更新哈希路径数据(也改变location的哈希值)、绑定函数监听hashchange事件

createHashHistory.js创建hashHistory对象,内含变更页面路径到更新location数据到执行绑点函数的一般方法

/*---------- HashProtocol.js模块 -----------*/
'use strict';

exports.__esModule = true;
exports.replaceLocation = exports.pushLocation = exports.startListener = exports.getCurrentLocation = exports.go = exports.getUserConfirmation = undefined;

var _BrowserProtocol = require('./BrowserProtocol');

// HashProtocol.getUserConfirmation(message,callback) 在callback回调中定制执行window.confirm(message)
Object.defineProperty(exports, 'getUserConfirmation', {
    enumerable: true,
    get: function get() {
        return _BrowserProtocol.getUserConfirmation;
    }
});

// 内部调用window.history.go处理传参
Object.defineProperty(exports, 'go', {
    enumerable: true,
    get: function get() {
        return _BrowserProtocol.go;
    }
});

// node模块;warning(condition,format,args) 开发环境下,condition为否值时,使用args替换报错字符串模板format输出
// _warning2.default引用_warning,即node模块warning
var _warning = require('warning');
var _warning2 = _interopRequireDefault(_warning);

// 创建、比较location数据对象方法集
var _LocationUtils = require('./LocationUtils');

// 封装浏览器绑定、移除函数方法,以及路径相关方法能力检测
var _DOMUtils = require('./DOMUtils');

// 使用window.sessionStorage(当前页面有效)存储及读取location.state数据
var _DOMStateStorage = require('./DOMStateStorage');

// 路径操作工具函数集
var _PathUtils = require('./PathUtils');

// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var HashChangeEvent = 'hashchange';

// 获取window.location.href下“#”后的内容,作为哈希路径hashPath
var getHashPath = function getHashPath() {
    var href = window.location.href;
    var hashIndex = href.indexOf('#');
    return hashIndex === -1 ? '' : href.substring(hashIndex + 1);
};

//  通过windows.location.hash=path重新赋值哈希路径
var pushHashPath = function pushHashPath(path) {
    return window.location.hash = path;
};

// 通过拼接window.location.href重新赋值哈希路径
var replaceHashPath = function replaceHashPath(path) {
    var hashIndex = window.location.href.indexOf('#');

    window.location.replace(window.location.href.slice(0, hashIndex >= 0 ? hashIndex : 0) + '#' + path);
};

// 通过window.location.href、window.sessionStorage获取location哈希路径数据{pathname,search,hash,state,action,key}
// queryKey是hashPath中查询字符串的键,用于获取window.sessionStorage存储location.state数据的key值
var getCurrentLocation = exports.getCurrentLocation = function getCurrentLocation(pathCoder, queryKey) {
    // 获取哈希路径hashPath
    var path = pathCoder.decodePath(getHashPath());

    // _PathUtils.getQueryStringValueFromPath获取哈希路径hashPath下查询字符串search中,queryKey键的值
    var key = (0, _PathUtils.getQueryStringValueFromPath)(path, queryKey);

    var state = void 0;
    if (key) {
        // hashPath下下查询字符串中含有key键值,替换为"?"或"#",重新拼接路径输出
        path = (0, _PathUtils.stripQueryStringValueFromPath)(path, queryKey);
        // 读取window.sessionStorage(当前页面有效)存储的location.state数据
        state = (0, _DOMStateStorage.readState)(key);
    }

    var init = (0, _PathUtils.parsePath)(path);
    init.state = state;

    // _LocationUtils.createLocation(obj={pathname,search,hash,state},action=undefined,key)
    // 将location数据转化为对象形式输出{pathname,search,hash,state,action,key}
    return (0, _LocationUtils.createLocation)(init, undefined, key);
};

var prevLocation = void 0;// 闭包缓存之前存储的location哈希路径数据

// 绑定函数listener(以哈希路径数据{pathname,search,hash,state,action,key}为参数),监听hashChange事件
var startListener = exports.startListener = function startListener(listener, pathCoder, queryKey) {
    var handleHashChange = function handleHashChange() {
        var path = getHashPath();
        var encodedPath = pathCoder.encodePath(path);

        if (path !== encodedPath) {
            // 确保哈希录路径按pathCoder定义格式书写,否则重写哈希路径(同时将触发hashChange事件,所有浏览器兼容)
            replaceHashPath(encodedPath);
        } else {
            var currentLocation = getCurrentLocation(pathCoder, queryKey);

            if (prevLocation && currentLocation.key && prevLocation.key === currentLocation.key) return;

            prevLocation = currentLocation;

            // 执行listener回调函数,以哈希路径数据{pathname,search,hash,state,action,key}为参数
            listener(currentLocation);
        }
    };

    // 确保哈希录路径按pathCoder定义格式书写,否则重写哈希路径(同时将触发hashChange事件,所有浏览器兼容)
    var path = getHashPath();
    var encodedPath = pathCoder.encodePath(path);
    if (path !== encodedPath) replaceHashPath(encodedPath);

    // 绑定函数,监听hashChange事件
    (0, _DOMUtils.addEventListener)(window, HashChangeEvent, handleHashChange);

    // 返回移除函数
    return function () {
        return (0, _DOMUtils.removeEventListener)(window, HashChangeEvent, handleHashChange);
    };
};

// 由哈希路径数据location、哈希路径书写约定pathCoder、哈希路径下查询字符串的键queryKey,重新获取哈希路径
// 哈希路径hashPath已变更,则使用windows.location.hash=path重新赋值当前hash路径,不刷新页面
var updateLocation = function updateLocation(location, pathCoder, queryKey, updateHash) {
    var state = location.state;
    var key = location.key;

    // 由location拼接生成路径,实质是哈希路径hashPath内容,hash路径部分符合pathCoder约定的书写格式
    var path = pathCoder.encodePath((0, _PathUtils.createPath)(location));

    if (state !== undefined) {
        // hashPath下路径下添加queryKey+"="+key形式的查询字符串
        path = (0, _PathUtils.addQueryStringValueToPath)(path, queryKey, key);
        // window.sessionStorage存储{[key]:state}形式的location哈希路径数据
        (0, _DOMStateStorage.saveState)(key, state);
    }

    prevLocation = location;

    // 哈希路径hashPath已变更,则使用windows.location.hash=path重新赋值当前hash路径,不刷新页面
    updateHash(path);
};

// 哈希路径重新赋值,并使用window.sessionStorage存储{[key]:state}形式的location哈希路径数据
var pushLocation = exports.pushLocation = function pushLocation(location, pathCoder, queryKey) {
    return updateLocation(location, pathCoder, queryKey, function (path) {
        // 哈希路径hashPath已变更,则使用windows.location.hash=path重新赋值当前hash路径,不刷新页面
        // 哈希路径未变更,开发环境下警告提示
        if (getHashPath() !== path) {
            pushHashPath(path);
        } else {
            process.env.NODE_ENV !== 'production' ? 
                (0, _warning2.default)(false, 'You cannot PUSH the same path using hash history') : void 0;
        }
    });
};

// 替换哈希路径,并使用window.sessionStorage存储{[key]:state}形式的location哈希路径数据
var replaceLocation = exports.replaceLocation = function replaceLocation(location, pathCoder, queryKey) {
    return updateLocation(location, pathCoder, queryKey, function (path) {
        if (getHashPath() !== path) replaceHashPath(path);
    });
};



/*---------- createHashHistory.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; 
};

// node模块;warning(condition,format,args) 开发环境下,condition为否值时,使用args替换报错字符串模板format输出
// _warning2.default引用_warning,即node模块warning
var _warning = require('warning');
var _warning2 = _interopRequireDefault(_warning);

// 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);

// 判断平台是否为浏览器环境,可以操作dom节点
var _ExecutionEnvironment = require('./ExecutionEnvironment');

// 封装浏览器绑定、移除函数方法,以及路径相关方法能力检测
var _DOMUtils = require('./DOMUtils');

// 哈希路径数据操作方法集,包括获取当前的哈希路径数据、更新哈希路径数据(也改变location的哈希值)、绑定函数监听hashchange事件
var _HashProtocol = require('./HashProtocol');
var HashProtocol = _interopRequireWildcard(_HashProtocol);

// 创建histroy方法,内含变更页面路径到更新location数据到执行绑点函数的一般方法,createHashHistory将重写个中方法
var _createHistory = require('./createHistory');
var _createHistory2 = _interopRequireDefault(_createHistory);

// obj为history模块内建模块(对象),原样输出;否则转化为纯对象输出
function _interopRequireWildcard(obj) { 
    if (obj && obj.__esModule) { 
        return obj; 
    } else { 
        var newObj = {}; 
        if (obj != null) { 
            for (var key in obj) { 
                if (Object.prototype.hasOwnProperty.call(obj, key)) 
                    newObj[key] = obj[key]; 
            } 
        } 
        newObj.default = obj; 
        return newObj; 
    } 
}

// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interopRequireDefault(obj) { 
    return obj && obj.__esModule ? obj : { default: obj }; 
}

var DefaultQueryKey = '_k';

var addLeadingSlash = function addLeadingSlash(path) {
  return path.charAt(0) === '/' ? path : '/' + path;
};

// 定义hash路径书写格式,hashbang以"!"起始,noslash直接跟路径,slash以"/"起始
var HashPathCoders = {
    hashbang: {
        encodePath: function encodePath(path) {
          return path.charAt(0) === '!' ? path : '!' + path;
        },
        decodePath: function decodePath(path) {
          return path.charAt(0) === '!' ? path.substring(1) : path;
        }
    },
    noslash: {
        encodePath: function encodePath(path) {
            return path.charAt(0) === '/' ? path.substring(1) : path;
        },
        decodePath: addLeadingSlash
    },
    // 以"/"作为window.location.href下“#”后哈希路径hashPath的起始符
    slash: {
        encodePath: addLeadingSlash,
        decodePath: addLeadingSlash
    }
};

// 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 = function createHashHistory() {
    // 首参有效
    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

    // 不是浏览器环境报错
    !_ExecutionEnvironment.canUseDOM ? process.env.NODE_ENV !== 'production' ? 
        (0, _invariant2.default)(false, 'Hash history needs a DOM') 
        : (0, _invariant2.default)(false) 
        : void 0;

    var queryKey = options.queryKey;
    var hashType = options.hashType;

    // options.queryKey设置为false,报错
    process.env.NODE_ENV !== 'production' ? 
        (0, _warning2.default)(queryKey !== false, 
            'Using { queryKey: false } no longer works. Instead, just don\'t ' 
            + 'use location state if you don\'t want a key in your URL query string') 
        : void 0;

    if (typeof queryKey !== 'string') queryKey = DefaultQueryKey;

    if (hashType == null) hashType = 'slash';

    // options.hashType不是hashbang、noslash、slash中的一个,报错
    if (!(hashType in HashPathCoders)) {
        process.env.NODE_ENV !== 'production' ? 
            (0, _warning2.default)(false, 'Invalid hash type: %s', hashType) : void 0;

        hashType = 'slash';
    }

    var pathCoder = HashPathCoders[hashType];

    // HashProtocol.getUserConfirmation(message,callback) 在callback回调中定制执行window.confirm(message)
    var getUserConfirmation = HashProtocol.getUserConfirmation;

    // 通过window.location.href、window.sessionStorage获取location数据{pathname,search,hash,state,action,key}
    var getCurrentLocation = function getCurrentLocation() {
      return HashProtocol.getCurrentLocation(pathCoder, queryKey);
    };

    // 哈希路径重新赋值,并使用window.sessionStorage存储{[key]:state}形式的location哈希路径数据
    var pushLocation = function pushLocation(location) {
        return HashProtocol.pushLocation(location, pathCoder, queryKey);
    };

    // 替换哈希路径,并使用window.sessionStorage存储{[key]:state}形式的location哈希路径数据
    var replaceLocation = function replaceLocation(location) {
        return HashProtocol.replaceLocation(location, pathCoder, queryKey);
    };

    var history = (0, _createHistory2.default)(_extends({
        getUserConfirmation: getUserConfirmation }, options, {
        getCurrentLocation: getCurrentLocation,
        pushLocation: pushLocation,
        replaceLocation: replaceLocation,
        go: HashProtocol.go// 内部调用window.history.go处理传参
    }));

    var listenerCount = 0,// 用以限制history.transitionTo函数绑定到hashChange事件上只一次
        stopListener = void 0;

    // 将history.transitionTo函数绑定到hashChange事件上
    // hashChange事件触发时,执行钩子函数,及变更浏览器location及window.sessionStorage存储
    var startListener = function startListener(listener, before) {
        if (++listenerCount === 1){
            // 将history.transitionTo函数绑定到hashChange事件上
            // 迭代执行前置钩子beforeListeners,变更浏览器location及window.sessionStorage存储的过程
            // 更新allKeys页面路径修改记录,执行listener后置钩子
            stopListener = HashProtocol.startListener(history.transitionTo, pathCoder, queryKey);
        }

        // 添加前置钩子或后置钩子
        var unlisten = before ? history.listenBefore(listener) : history.listen(listener);

        // 移除绑定hashChange事件上的history.transitionTo函数及listener钩子函数
        return function () {
            unlisten();

            if (--listenerCount === 0) stopListener();
        };
    };

    // 将history.transitionTo函数绑定到hashChange事件上,并添加前置钩子
    // hashChange事件触发时,执行钩子函数,及变更浏览器location及window.sessionStorage存储
    var listenBefore = function listenBefore(listener) {
        return startListener(listener, true);
    };

    // 将history.transitionTo函数绑定到hashChange事件上,并添加后置钩子
    // hashChange事件触发时,执行钩子函数,及变更浏览器location及window.sessionStorage存储
    var listen = function listen(listener) {
        return startListener(listener, false);
    };

    // 能力检测,判断window.history.go(n)方法更新哈希路径是否会导致页面重新加载
    var goIsSupportedWithoutReload = (0, _DOMUtils.supportsGoWithoutReloadUsingHash)();

    // 内部调用window.history.go处理传参
    var go = function go(n) {
        // window.history.go(n)方法更新哈希路径将导致页面重新加载,报错
        process.env.NODE_ENV !== 'production' ? 
            (0, _warning2.default)(goIsSupportedWithoutReload, 
                'Hash history go(n) causes a full page reload in this browser') 
            : void 0;

        history.go(n);
    };

    // 生成哈希路径
    var createHref = function createHref(path) {
        return '#' + pathCoder.encodePath(history.createHref(path));
    };

    return _extends({}, history, {
        listenBefore: listenBefore,
        listen: listen,
        go: go,
        createHref: createHref
    });
};

exports.default = createHashHistory;

BrowserProtocol.js获取当前的路径数据、创建路径数据、更新路径数据(也改变页面的location)、绑定函数监听popState或hashchange事件

RefreshProtocol.js.js获取当前的路径数据、更新路径数据(也改变页面的location)

createBrowserHistory.js创建hashHistory对象,内含变更页面路径到更新location数据到执行绑点函数的一般方法

/*---------- BrowserProtocol.js模块 -----------*/
'use strict';

exports.__esModule = true;
exports.go = exports.replaceLocation = exports.pushLocation = exports.startListener = exports.getUserConfirmation = exports.getCurrentLocation = undefined;

// 创建、比较location数据对象方法集
var _LocationUtils = require('./LocationUtils');

// 封装浏览器绑定、移除函数方法,以及路径相关方法能力检测
var _DOMUtils = require('./DOMUtils');

// 使用window.sessionStorage(当前页面有效)存储及读取location.state数据
var _DOMStateStorage = require('./DOMStateStorage');

// 路径操作工具函数集
var _PathUtils = require('./PathUtils');

// 判断平台是否为浏览器环境,可以操作dom节点
var _ExecutionEnvironment = require('./ExecutionEnvironment');

var PopStateEvent = 'popstate';
var HashChangeEvent = 'hashchange';

// _DOMUtils.supportsPopstateOnHashchange能力检测,判断哈希路径改变时是否会触发popstate事件
var needsHashchangeListener = _ExecutionEnvironment.canUseDOM && !(0, _DOMUtils.supportsPopstateOnHashchange)();

// 通过{state}生成location数据
var _createLocation = function _createLocation(historyState) {
    var key = historyState && historyState.key;

    return (0, _LocationUtils.createLocation)({
        pathname: window.location.pathname,
        search: window.location.search,
        hash: window.location.hash,
        state: key ? (0, _DOMStateStorage.readState)(key) : undefined
    }, undefined, key);
};

// 获取html5的window.history.state数据,用以生成生成location数据
var getCurrentLocation = exports.getCurrentLocation = function getCurrentLocation() {
    var historyState = void 0;
    try {
        historyState = window.history.state || {};
    } catch (error) {
        // IE 11 sometimes throws when accessing window.history.state
        historyState = {};
    }

    return _createLocation(historyState);
};

// BrowserProtocol.getUserConfirmation(message,callback) 在callback回调中定制执行window.confirm(message)
var getUserConfirmation = exports.getUserConfirmation = function getUserConfirmation(message, callback) {
    return callback(window.confirm(message));
};

// 在popstate、hashChange事件上绑定listener(location)回调
var startListener = exports.startListener = function startListener(listener) {
    var handlePopState = function handlePopState(event) {
        if (event.state !== undefined) // Ignore extraneous popstate events in WebKit
            listener(_createLocation(event.state));
    };

    // 绑定listener(location)回调,H5的popState事件在history.pushState|repleaceState方法调用时触发
    (0, _DOMUtils.addEventListener)(window, PopStateEvent, handlePopState);

    var handleUnpoppedHashChange = function handleUnpoppedHashChange() {
      return listener(getCurrentLocation());
    };

    // 浏览器不支持popstate事件(重设哈希路径时触发),换用hashChange事件绑定listener(location)回调
    if (needsHashchangeListener) {
        (0, _DOMUtils.addEventListener)(window, HashChangeEvent, handleUnpoppedHashChange);
    }

    // 事件解绑
    return function () {
        (0, _DOMUtils.removeEventListener)(window, PopStateEvent, handlePopState);

        if (needsHashchangeListener) {
            (0, _DOMUtils.removeEventListener)(window, HashChangeEvent, handleUnpoppedHashChange);
        }
    };
};

// window.sessionStorage存储{[key]:state}形式的location路径数据
// pushLocation方法调用时,window.history.pushState({key},_PathUtils.createPath(location))
// replaceLocation方法调用时,window.history.replaceState({key},_PathUtils.createPath(location))
var updateLocation = function updateLocation(location, updateState) {
    var state = location.state;
    var key = location.key;

    if (state !== undefined) (0, _DOMStateStorage.saveState)(key, state);

    updateState({ key: key }, (0, _PathUtils.createPath)(location));
};

// window.sessionStorage存储{[key]:state}形式的location路径数据
// window.history.state存储{key:location.key},页面路径后加"/"+path
var pushLocation = exports.pushLocation = function pushLocation(location) {
    return updateLocation(location, function (state, path) {
        return window.history.pushState(state, null, path);
    });
};

// window.sessionStorage存储{[key]:state}形式的location路径数据
// window.history.state存储{key:location.key},页面路径后加"/"+path
var replaceLocation = exports.replaceLocation = function replaceLocation(location) {
    return updateLocation(location, function (state, path) {
        return window.history.replaceState(state, null, path);
    });
};

// 内部调用window.history.go处理传参
var go = exports.go = function go(n) {
    if (n) window.history.go(n);
};



/*---------- RefreshProtocol.js模块 -----------*/
'use strict';

exports.__esModule = true;
exports.replaceLocation = exports.pushLocation = exports.getCurrentLocation = exports.go = exports.getUserConfirmation = undefined;

var _BrowserProtocol = require('./BrowserProtocol');

// HashProtocol.getUserConfirmation(message,callback) 在callback回调中定制执行window.confirm(message)
Object.defineProperty(exports, 'getUserConfirmation', {
    enumerable: true,
    get: function get() {
        return _BrowserProtocol.getUserConfirmation;
    }
});

// 内部调用window.history.go处理传参
Object.defineProperty(exports, 'go', {
    enumerable: true,
    get: function get() {
        return _BrowserProtocol.go;
    }
});

// 创建、比较location数据对象方法集
var _LocationUtils = require('./LocationUtils');

// 路径操作工具函数集
var _PathUtils = require('./PathUtils');

// 获取当前的路径数据{pathname,search,hash,state,action,key}
var getCurrentLocation = exports.getCurrentLocation = function getCurrentLocation() {
    return (0, _LocationUtils.createLocation)(window.location);
};

// 使用window.location.href=path变更页面路径
var pushLocation = exports.pushLocation = function pushLocation(location) {
    window.location.href = (0, _PathUtils.createPath)(location);
    return false;
};

// 使用window.location.replace(path)变更页面路径
var replaceLocation = exports.replaceLocation = function replaceLocation(location) {
    window.location.replace((0, _PathUtils.createPath)(location));
    return false;
};



/*---------- createBrowserHistory.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; 
};

// 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);

// 判断平台是否为浏览器环境,可以操作dom节点
var _ExecutionEnvironment = require('./ExecutionEnvironment');

// 路径数据操作方法集,包括获取当前的路径数据、更新路径数据(也改变location的路径值)、绑定函数监听hashchange或popState事件
var _BrowserProtocol = require('./BrowserProtocol');
var BrowserProtocol = _interopRequireWildcard(_BrowserProtocol);

// 路径数据操作方法集,包括获取当前的路径数据、改变页面路径location
var _RefreshProtocol = require('./RefreshProtocol');
var RefreshProtocol = _interopRequireWildcard(_RefreshProtocol);

// 封装浏览器绑定、移除函数方法,以及路径相关方法能力检测
var _DOMUtils = require('./DOMUtils');

// 创建histroy方法,内含变更页面路径到更新location数据到执行绑点函数的一般方法,createBrowserHistory将重写个中方法
var _createHistory = require('./createHistory');
var _createHistory2 = _interopRequireDefault(_createHistory);

// obj为history模块内建模块(对象),原样输出;否则转化为纯对象输出
function _interopRequireWildcard(obj) { 
    if (obj && obj.__esModule) { 
        return obj; 
    } else { 
        var newObj = {}; 
        if (obj != null) { 
            for (var key in obj) { 
                if (Object.prototype.hasOwnProperty.call(obj, key)) 
                    newObj[key] = obj[key]; 
            } 
        } 
        newObj.default = obj; 
        return newObj; 
    } 
}

// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interopRequireDefault(obj) { 
    return obj && obj.__esModule ? obj : { default: obj }; 
}

// 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 = function createBrowserHistory() {
    // 首参有效
    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

    // 不是浏览器环境报错
    !_ExecutionEnvironment.canUseDOM ? process.env.NODE_ENV !== 'production' ? 
        (0, _invariant2.default)(false, 'Hash history needs a DOM') 
        : (0, _invariant2.default)(false) 
        : void 0;

    // _DOMUtils.supportsHistory能力检测,判断浏览器是否支持HTML5 history API
    var useRefresh = options.forceRefresh || !(0, _DOMUtils.supportsHistory)();
    // 不支持h5的histroy方法或者options.forceRefresh为真
    // 使用window.location.href(path)或window.location.replace(path)更新页面路径
    // 否则以BrowserProtocol中window.history.pushState、window.history.replaceState更新页面路径
    var Protocol = useRefresh ? RefreshProtocol : BrowserProtocol;

    var getUserConfirmation = Protocol.getUserConfirmation;
    var getCurrentLocation = Protocol.getCurrentLocation;
    var pushLocation = Protocol.pushLocation;
    var replaceLocation = Protocol.replaceLocation;
    var go = Protocol.go;


    var history = (0, _createHistory2.default)(_extends({
        getUserConfirmation: getUserConfirmation }, options, {
        getCurrentLocation: getCurrentLocation,
        pushLocation: pushLocation,
        replaceLocation: replaceLocation,
        go: go// 内部调用window.history.go处理传参
    }));

    var listenerCount = 0,
        stopListener = void 0;

    // 将history.transitionTo函数绑定到popState或hashChange事件上
    // popState或hashChange事件触发时,执行钩子函数,及变更浏览器location及window.sessionStorage存储
    var startListener = function startListener(listener, before) {
        // 将history.transitionTo函数绑定到popState或hashChange事件上
        // 迭代执行前置钩子beforeListeners,变更浏览器location及window.sessionStorage存储的过程
        // 更新allKeys页面路径修改记录,执行listener后置钩子
        if (++listenerCount === 1) stopListener = BrowserProtocol.startListener(history.transitionTo);

        // 添加前置钩子或后置钩子
        var unlisten = before ? history.listenBefore(listener) : history.listen(listener);

        // 移除绑定hashChange事件上的history.transitionTo函数及listener钩子函数
        return function () {
            unlisten();

            if (--listenerCount === 0) stopListener();
        };
    };

    // 将history.transitionTo函数绑定到popState或hashChange事件上,并添加前置钩子
    // popState或hashChange事件触发时,执行钩子函数,及变更浏览器location及window.sessionStorage存储
    var listenBefore = function listenBefore(listener) {
        return startListener(listener, true);
    };

    // 将history.transitionTo函数绑定到popState或hashChange事件上,并添加后置钩子
    // popState或hashChange事件触发时,执行钩子函数,及变更浏览器location及window.sessionStorage存储
    var listen = function listen(listener) {
        return startListener(listener, false);
    };

    return _extends({}, history, {
        listenBefore: listenBefore,
        listen: listen
    });
};

exports.default = createBrowserHistory;

createMemoryHistory.js使用js闭包缓存特点构建{key:state}存储机制,存储在闭包变量storage中

'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模块;warning(condition,format,args) 开发环境下,condition为否值时,使用args替换报错字符串模板format输出
// _warning2.default引用_warning,即node模块warning
var _warning = require('warning');
var _warning2 = _interopRequireDefault(_warning);

// 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);

// 创建、比较location数据对象方法集
var _LocationUtils = require('./LocationUtils');

// 路径操作工具函数集
var _PathUtils = require('./PathUtils');

// 创建histroy方法,内含变更页面路径到更新location数据到执行绑点函数的一般方法,createHashHistory将重写个中方法
var _createHistory = require('./createHistory');
var _createHistory2 = _interopRequireDefault(_createHistory);

// location.action允许的值
var _Actions = require('./Actions');

// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interopRequireDefault(obj) { 
    return obj && obj.__esModule ? obj : { default: obj }; 
}

// 将[{key:state}]转化为对象形式{key:state}
var createStateStorage = function createStateStorage(entries) {
    return entries.filter(function (entry) {
        return entry.state;
    }).reduce(function (memo, entry) {
        memo[entry.key] = entry.state;
        return memo;
    }, {});
};

// 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 = function createMemoryHistory() {
    // 首参有效
    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

    if (Array.isArray(options)) {
        options = { entries: options };
    } else if (typeof options === 'string') {
        options = { entries: [options] };
    }

    // 通过options.current获取location数据{pathname,search,hash,state,action,key}
    var getCurrentLocation = function getCurrentLocation() {
        var entry = entries[current];// options.current
        var path = (0, _PathUtils.createPath)(entry);

        var key = void 0,
            state = void 0;
        if (entry.key) {
          key = entry.key;
          state = readState(key);
        }

        var init = (0, _PathUtils.parsePath)(path);

        return (0, _LocationUtils.createLocation)(_extends({}, init, { state: state }), undefined, key);
    };

    // 是否支持跳转到entries[current+n]路径对象下
    var canGo = function canGo(n) {
        var index = current + n;
        return index >= 0 && index < entries.length;
    };

    // 迭代执行前置钩子beforeListeners,变更浏览器location及storage存储的过程
    // 更新allKeys页面路径修改记录,执行listener后置钩子
    var go = function go(n) {
        if (!n) return;

        if (!canGo(n)) {
            process.env.NODE_ENV !== 'production' ? 
                (0, _warning2.default)(false, 'Cannot go(%s) there is not enough history', n) : void 0;

            return;
        }

        current += n;
        var currentLocation = getCurrentLocation();

        history.transitionTo(_extends({}, currentLocation, { action: _Actions.POP }));
    };

    // entries、storage添加一条路径记录
    var pushLocation = function pushLocation(location) {
        current += 1;

        if (current < entries.length) entries.splice(current);

        entries.push(location);

        saveState(location.key, location.state);
    };

    // entries、storage替换一条路径记录
    var replaceLocation = function replaceLocation(location) {
        entries[current] = location;
        saveState(location.key, location.state);
    };

    var history = (0, _createHistory2.default)(_extends({}, options, {
        getCurrentLocation: getCurrentLocation,
        pushLocation: pushLocation,
        replaceLocation: replaceLocation,
        go: go
    }));

    var _options = options;
    var entries = _options.entries;// 闭包缓存,[{key:state}]数组形式
    var current = _options.current;// 闭包缓存

    if (typeof entries === 'string') {
        entries = [entries];
    } else if (!Array.isArray(entries)) {
        entries = ['/'];
    }

    // 将options.entries中的字符串内容解析为{key}对象形式
    entries = entries.map(function (entry) {
        return (0, _LocationUtils.createLocation)(entry);
    });

    // current默认赋值为options.entries.length-1;不在options.entries长度范围内,报错
    if (current == null) {
        current = entries.length - 1;
    } else {
        !(current >= 0 && current < entries.length) ? process.env.NODE_ENV !== 'production' 
            ? (0, _invariant2.default)(false, 'Current index must be >= 0 and < %s, was %s', 
                entries.length, current) : (0, _invariant2.default)(false) 
            : void 0;
    }

    // 将options.entries的值[{key:state}]转化为对象形式{key:state}输出,闭包缓存,{key:state}对象形式
    var storage = createStateStorage(entries);

    // 更新storage[key]存储的state数据
    var saveState = function saveState(key, state) {
        return storage[key] = state;
    };

    // 获取storage[key]存储的state数据
    var readState = function readState(key) {
        return storage[key];
    };

    return _extends({}, history, {
        canGo: canGo
    });
};

// 使用js闭包缓存特点构建{key:state}存储机制
exports.default = createMemoryHistory;

useBasename.js通过装饰createBrowserHistory、createHashHistory、createMemoryHistory,达到操作location数据对象时分离basename的目的

'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; 
};

// runTransitionHook(hook,location,callback)
// 执行hook(location),callback为hook函数执行完成后的回调
// 执行hook(location,callback),callback在hook函数内部调用,hook不能返回真值
var _runTransitionHook = require('./runTransitionHook');
var _runTransitionHook2 = _interopRequireDefault(_runTransitionHook);

// 路径操作工具函数集
var _PathUtils = require('./PathUtils');

// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var useBasename = function useBasename(createHistory) {
    // 装饰createHistory函数,操作location数据对象时分离basename,返回装饰函数
    return function () {
        var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

        var history = createHistory(options);
        var basename = options.basename;

        // location路径数据对象添加basename属性,pathname根据basename进行调整
        var addBasename = function addBasename(location) {
            if (!location) return location;

            if (basename && location.basename == null) {
                if (location.pathname.indexOf(basename) === 0) {
                    location.pathname = location.pathname.substring(basename.length);
                    location.basename = basename;

                    if (location.pathname === '') location.pathname = '/';
                } else {
                    location.basename = '';
                }
            }

            return location;
        };

        // 操作pathname的时候,将basename前插到pathname字符串前
        var prependBasename = function prependBasename(location) {
            if (!basename) return location;

            var object = typeof location === 'string' ? (0, _PathUtils.parsePath)(location) : location;
            var pname = object.pathname;
            var normalizedBasename = basename.slice(-1) === '/' ? basename : basename + '/';
            var normalizedPathname = pname.charAt(0) === '/' ? pname.slice(1) : pname;
            var pathname = normalizedBasename + normalizedPathname;

            return _extends({}, object, {
                pathname: pathname
            });
        };

        // 读取location数据对象时,分离basename作为单一的属性
        var getCurrentLocation = function getCurrentLocation() {
          return addBasename(history.getCurrentLocation());
        };

        var listenBefore = function listenBefore(hook) {
            return history.listenBefore(function (location, callback) {
                return (0, _runTransitionHook2.default)(hook, addBasename(location), callback);
            });
        };

        var listen = function listen(listener) {
          return history.listen(function (location) {
            return listener(addBasename(location));
          });
        };

        // 操作pathname时,将basename前插到pathname字符串前
        var push = function push(location) {
          return history.push(prependBasename(location));
        };

        var replace = function replace(location) {
          return history.replace(prependBasename(location));
        };

        var createPath = function createPath(location) {
          return history.createPath(prependBasename(location));
        };

        var createHref = function createHref(location) {
          return history.createHref(prependBasename(location));
        };

        var createLocation = function createLocation(location) {
            for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
                args[_key - 1] = arguments[_key];
            }

            return addBasename(history.createLocation.apply(history, [prependBasename(location)].concat(args)));
        };

        return _extends({}, history, {
            getCurrentLocation: getCurrentLocation,
            listenBefore: listenBefore,
            listen: listen,
            push: push,
            replace: replace,
            createPath: createPath,
            createHref: createHref,
            createLocation: createLocation
        });
    };
};

// 装饰createHistory函数,操作location数据对象时分离basename,返回装饰函数
exports.default = useBasename;

useBeforeUnload.js通过装饰createBrowserHistory、createHashHistory、createMemoryHistory,添加listenBeforeUnload方法,达到监听'beforeunload'弹出警告的目的

'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 _DOMUtils = require('./DOMUtils');

// 判断平台是否为浏览器环境,可以操作dom节点
var _ExecutionEnvironment = require('./ExecutionEnvironment');

// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interopRequireDefault(obj) { 
    return obj && obj.__esModule ? obj : { default: obj }; 
}

var startListener = function startListener(getPromptMessage) {
    var handleBeforeUnload = function handleBeforeUnload(event) {
        var message = getPromptMessage();

        if (typeof message === 'string') {
            (event || window.event).returnValue = message;
            return message;
        }

        return undefined;
    };

    (0, _DOMUtils.addEventListener)(window, 'beforeunload', handleBeforeUnload);

    return function () {
        return (0, _DOMUtils.removeEventListener)(window, 'beforeunload', handleBeforeUnload);
    };
};

var useBeforeUnload = function useBeforeUnload(createHistory) {
    !_ExecutionEnvironment.canUseDOM ? process.env.NODE_ENV !== 'production' ? 
        (0, _invariant2.default)(false, 'useBeforeUnload only works in DOM environments') : 
        (0, _invariant2.default)(false) : void 0;

    return function (options) {
        var history = createHistory(options);

        var listeners = [];
        var stopListener = void 0;

        var getPromptMessage = function getPromptMessage() {
            var message = void 0;
            for (var i = 0, len = listeners.length; message == null && i < len; ++i) {
                message = listeners[i].call();
            }return message;
        };

        // 关闭页面时执行listener,触发'beforeunload',listener返回值作为弹出警告,返回移除监听事件函数
        var listenBeforeUnload = function listenBeforeUnload(listener) {
            if (listeners.push(listener) === 1) stopListener = startListener(getPromptMessage);

            return function () {
                listeners = listeners.filter(function (item) {
                    return item !== listener;
                });

                if (listeners.length === 0 && stopListener) {
                    stopListener();
                    stopListener = null;
                }
            };
        };

        return _extends({}, history, {
            listenBeforeUnload: listenBeforeUnload
        });
    };
};

// 装饰createHistory,添加listenBeforeUnload方法,返回值接受options作为参数,供createHistory(options)调用
// listenBeforeUnload方法,关闭页面时执行listener,触发'beforeunload',listener返回值作为弹出警告,返回移除监听事件函数
exports.default = useBeforeUnload;

useQueries.js通过装饰createBrowserHistory、createHashHistory、createMemoryHistory,达到操作location数据对象时分离location.query(对象化的查询字符串)的目的

'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 _queryString = require('query-string');

// runTransitionHook(hook,location,callback)
// 执行hook(location),callback为hook函数执行完成后的回调
// 执行hook(location,callback),callback在hook函数内部调用,hook不能返回真值
var _runTransitionHook = require('./runTransitionHook');
var _runTransitionHook2 = _interopRequireDefault(_runTransitionHook);

// 创建、比较location数据对象方法集
var _LocationUtils = require('./LocationUtils');

// 路径操作工具函数集
var _PathUtils = require('./PathUtils');

// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var defaultStringifyQuery = function defaultStringifyQuery(query) {
  return (0, _queryString.stringify)(query).replace(/%20/g, '+');
};

var defaultParseQueryString = _queryString.parse;

var useQueries = function useQueries(createHistory) {
    return function () {
        var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

        var history = createHistory(options);
        var stringifyQuery = options.stringifyQuery;
        var parseQueryString = options.parseQueryString;

        if (typeof stringifyQuery !== 'function') stringifyQuery = defaultStringifyQuery;

        if (typeof parseQueryString !== 'function') parseQueryString = defaultParseQueryString;

          // 序列化查询字符串location.search,获取location.query
          var decodeQuery = function decodeQuery(location) {
              if (!location) return location;

              if (location.query == null) location.query = parseQueryString(location.search.substring(1));

              return location;
          };

          // 反序列化查询字符串location.query,获取location.search
          var encodeQuery = function encodeQuery(location, query) {
              if (query == null) return location;

              var object = typeof location === 'string' ? (0, _PathUtils.parsePath)(location) : location;
              var queryString = stringifyQuery(query);
              var search = queryString ? '?' + queryString : '';

              return _extends({}, object, {
                  search: search
              });
          };

        // 读取search的时候,对location.search进行序列化操作,获取location.query
        var getCurrentLocation = function getCurrentLocation() {
            return decodeQuery(history.getCurrentLocation());
        };

        var listenBefore = function listenBefore(hook) {
            return history.listenBefore(function (location, callback) {
                return (0, _runTransitionHook2.default)(hook, decodeQuery(location), callback);
            });
        };

        var listen = function listen(listener) {
            return history.listen(function (location) {
                return listener(decodeQuery(location));
            });
        };

        // 操作search的时候,对location.query进行反序列化操作获取location.search
        var push = function push(location) {
          return history.push(encodeQuery(location, location.query));
        };

        var replace = function replace(location) {
            return history.replace(encodeQuery(location, location.query));
        };

        var createPath = function createPath(location) {
            return history.createPath(encodeQuery(location, location.query));
        };

        var createHref = function createHref(location) {
            return history.createHref(encodeQuery(location, location.query));
        };

        var createLocation = function createLocation(location) {
            for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
                args[_key - 1] = arguments[_key];
            }

            var newLocation = history.createLocation.apply(history, [encodeQuery(location, location.query)].concat(args));

            if (location.query) newLocation.query = (0, _LocationUtils.createQuery)(location.query);

            return decodeQuery(newLocation);
        };

        return _extends({}, history, {
            getCurrentLocation: getCurrentLocation,
            listenBefore: listenBefore,
            listen: listen,
            push: push,
            replace: replace,
            createPath: createPath,
            createHref: createHref,
            createLocation: createLocation
        });
    };
};

exports.default = useQueries;

index.js对外接口

'use strict';

exports.__esModule = true;
exports.locationsAreEqual = exports.Actions = exports.useQueries = exports.useBeforeUnload = exports.useBasename = exports.createMemoryHistory = exports.createHashHistory = exports.createHistory = undefined;

var _LocationUtils = require('./LocationUtils');

// 判断a、b两个location数据对象是否相同
Object.defineProperty(exports, 'locationsAreEqual', {
	enumerable: true,
	get: function get() {
		return _LocationUtils.locationsAreEqual;
	}
});

// 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('./createBrowserHistory');
var _createBrowserHistory2 = _interopRequireDefault(_createBrowserHistory);

// 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 _createHashHistory2 = require('./createHashHistory');
var _createHashHistory3 = _interopRequireDefault(_createHashHistory2);

// 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 _createMemoryHistory2 = require('./createMemoryHistory');
var _createMemoryHistory3 = _interopRequireDefault(_createMemoryHistory2);

// 装饰createHistory函数,操作location数据对象时分离basename,返回装饰函数
// 传参可以是createBrowserHistory、createHashHistory、createMemoryHistory
var _useBasename2 = require('./useBasename');
var _useBasename3 = _interopRequireDefault(_useBasename2);

// 装饰createHistory,添加listenBeforeUnload方法,返回值接受options作为参数,供createHistory(options)调用
// listenBeforeUnload方法,关闭页面时执行listener,触发'beforeunload',listener返回值作为弹出警告,返回移除监听事件函数
// 传参可以是createBrowserHistory、createHashHistory、createMemoryHistory
var _useBeforeUnload2 = require('./useBeforeUnload');
var _useBeforeUnload3 = _interopRequireDefault(_useBeforeUnload2);

// 同useBasename模块相仿,装饰createHistory函数,操作location数据对象时分离query查询字符串对象,返回装饰函数
// 传参可以是createBrowserHistory、createHashHistory、createMemoryHistory
var _useQueries2 = require('./useQueries');
var _useQueries3 = _interopRequireDefault(_useQueries2);

// location.action允许的值
var _Actions2 = require('./Actions');
var _Actions = _interopRequireWildcard(_Actions2);

// obj为history模块内建模块(对象),原样输出;否则转化为纯对象输出
function _interopRequireWildcard(obj) { 
    if (obj && obj.__esModule) { 
        return obj; 
    } else { 
        var newObj = {}; 
        if (obj != null) { 
            for (var key in obj) { 
                if (Object.prototype.hasOwnProperty.call(obj, key)) 
                    newObj[key] = obj[key]; 
            } 
        } 
        newObj.default = obj; 
        return newObj; 
    } 
}

// obj为history模块内建模块(函数),原样输出;否则将函数挂载在default方法上
function _interopRequireDefault(obj) { 
    return obj && obj.__esModule ? obj : { default: obj }; 
}

exports.createHistory = _createBrowserHistory2.default;
exports.createHashHistory = _createHashHistory3.default;
exports.createMemoryHistory = _createMemoryHistory3.default;
exports.useBasename = _useBasename3.default;
exports.useBeforeUnload = _useBeforeUnload3.default;
exports.useQueries = _useQueries3.default;
exports.Actions = _Actions;

猜你喜欢

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