Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

san-router

Package Overview
Dependencies
Maintainers
2
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

san-router - npm Package Compare versions

Comparing version 1.2.4 to 2.0.0

2

dist/san-router.js

@@ -1,1 +0,1 @@

!function(t){function e(t){switch(typeof t){case"object":return t;case"string":return document.querySelector?document.querySelector(t):document.getElementById(t.replace(/#/i,""))}}function r(t){var e={hash:"",queryString:"",params:{},query:{},path:t},r=e.path.indexOf("#");r>=0&&(e.hash=e.path.slice(r+1),e.path=e.path.slice(0,r));var n=e.query,i=e.path.indexOf("?");if(i>=0){e.queryString=e.path.slice(i+1),e.path=e.path.slice(0,i);for(var o=e.queryString.split("&"),s=0;s<o.length;s++){var a=o[s],h=a.indexOf("="),c="";h>0&&(c=a.slice(h+1),a=a.slice(0,h));var u=decodeURIComponent(a);c=decodeURIComponent(c),n.hasOwnProperty(u)?n[u]=[].concat(n[u],c):n[u]=c}}return e}function n(t){if(!t)return"";if("string"==typeof t)return t;var e,r=t.query,n=t.params,i=t.path||"",o=[];if(i)for(var s=i.split("/"),a=0,h=s.length;a<h;a++){var c=s[a];if(/^:[a-z0-9_-]+$/.test(c)){e=!0;var u=c.slice(1);o.push(n&&n[u]||r&&r[u])}else o.push(c)}var p=t.queryString||"";if(0===p.indexOf("?")&&(p=p.slice(1)),!p&&r&&(!e||n)){var f=!0;for(var l in r)r.hasOwnProperty(l)&&(p+=(f?"":"&")+l+"="+encodeURIComponent(r[l]),f=!1)}return o.join("/")+(p&&"?")+p}function i(t,e){var n=r(t),i=r(e),o=n.path;if(0===o.indexOf("/"))return t;var s;if(o){var a=o.split("/"),h=i.path.split("/");h.pop();for(var c=0;c<a.length;c++){var u=a[c];switch(u){case"..":h.pop();break;case".":break;default:h.push(u)}}""!==h[0]&&h.unshift(""),s=h.join("/")}else s=i.path;return s+(n.queryString?"?"+n.queryString:"")}function o(){}function s(){var t=location.href.indexOf("#");return t<0?"/":location.href.slice(t+1)||"/"}function a(){this.current=s(),this.referrer="";var t=this;this.hashChangeHandler=function(){t.redirect(s())}}function h(){return location.pathname+location.search}function c(){this.current=h(),this.referrer="";var t=this;this.popstateHandler=function(){t.referrer=t.current,t.current=h(),t.fire("redirect",{url:t.current,referrer:t.referrer})}}function u(){return(++d).toString()}function p(t){return t.prototype&&(5===t.prototype.nodeType||"san-cmpt"===t.prototype._type)}function f(t){return function(e){function n(){v>0&&(h<t.listeners.length?(t.listeners[h].call(t,y,a.config),v>0&&i()):o())}function i(){v=1,h++,n()}function o(){if(s)t.doRoute(s,a);else for(var e=t.routeAlives.length;e--;)t.routeAlives[e].component.dispose(),t.routeAlives.splice(e,1)}for(var s,a=r(e.url),h=0;h<t.routes.length;h++){var c=t.routes[h],u=c.rule.exec(a.path);if(u){s=c;for(var p=c.keys||[],f=1;f<u.length;f++){var l=p[f]||f,d=u[f];a.query[l]=d,a.params[l]=d}a.referrer=e.referrer,a.config=c.config;break}}var h=0,v=1,y={url:e.url,hash:a.hash,queryString:a.queryString,query:a.query,params:a.params,path:a.path,referrer:a.referrer,config:a.config,resume:i,suspend:function(){v=0},stop:function(){v=-1}};n()}}function l(t){t=t||{};var e=t.mode||"hash";if("html5"===e&&!c.isSupport)throw new Error("[SAN-ROUTER ERROR] Your navigator doesn't supports HTML5!");this.routes=[],this.routeAlives=[],this.listeners=[],this.locatorRedirectHandler=f(this),this.setMode(e)}o.prototype.on=function(t,e){"function"==typeof e&&(this._eventListeners||(this._eventListeners={}),this._eventListeners[t]||(this._eventListeners[t]=[]),this._eventListeners[t].push(e))},o.prototype.un=function(t,e){if(this._eventListeners&&this._eventListeners[t])if(e)for(var r=this._eventListeners[t],n=r.length;n--;)r[n]===e&&r.splice(n,1);else this._eventListeners[t]=[]},o.prototype.fire=function(t,e){if(!t)throw new Error("No event type specified");var r=this._eventListeners&&this._eventListeners[t];if(r)for(var n=0;n<r.length;n++)r[n](e)},a.prototype=new o,a.prototype.constructor=a,a.prototype.start=function(){window.addEventListener&&window.addEventListener("hashchange",this.hashChangeHandler,!1),window.attachEvent&&window.attachEvent("onhashchange",this.hashChangeHandler)},a.prototype.stop=function(){window.removeEventListener&&window.removeEventListener("hashchange",this.hashChangeHandler,!1),window.detachEvent&&window.detachEvent("onhashchange",this.hashChangeHandler)},a.prototype.redirect=function(t,e){e=e||{},t=i(t,this.current);var r=this.current,n=t!==r;n?(this.referrer=r,this.current=t,location.hash=t):r=this.referrer,!n&&!e.force||e.silent||this.fire("redirect",{url:t,referrer:r})},a.prototype.reload=function(){this.redirect(this.current,{force:!0})},c.prototype=new o,c.prototype.constructor=c,c.prototype.start=function(){window.addEventListener("popstate",this.popstateHandler)},c.prototype.stop=function(){window.removeEventListener("popstate",this.popstateHandler)},c.prototype.redirect=function(t,e){e=e||{},t=i(t,this.current);var r=this.current,n=t!==r;n&&(this.referrer=r,this.current=t,history.pushState({},"",t)),!n&&!e.force||e.silent||this.fire("redirect",{url:t,referrer:r})},c.prototype.reload=function(){this.fire("redirect",{url:this.current,referrer:this.referrer})},c.isSupport="pushState"in window.history;var d=365611;l.prototype.listen=function(t){this.listeners.push(t)},l.prototype.unlisten=function(t){for(var e=this.listeners.length;e--;)this.listeners[e]===t&&this.listeners.splice(e,1)},l.prototype.start=function(){return this.isStarted||(this.isStarted=!0,this.locator.on("redirect",this.locatorRedirectHandler),this.locator.start(),this.locator.reload()),this},l.prototype.stop=function(){return this.locator.un("redirect",this.locatorRedirectHandler),this.locator.stop(),this.isStarted=!1,this},l.prototype.setMode=function(t){if(t=t.toLowerCase(),this.mode!==t){this.mode=t;var e=!1;switch(this.isStarted&&(this.stop(),e=!0),t){case"hash":this.locator=new a;break;case"html5":this.locator=new c}return e&&this.start(),this}},l.prototype.doRoute=function(t,e){for(var r=!1,n=this.routeAlives.length;n--;){var i=this.routeAlives[n];i.id===t.id?(i.component.data.set("route",e),"function"==typeof i.component.route&&i.component.route(),r=!0):(i.component.dispose(),this.routeAlives.splice(n,1))}if(!r)if(t.Component)if(p(t.Component))this.attachCmpt(t,e);else{var o=this;t.Component().then(function(r){p(r)?t.Component=r:r.__esModule&&p(r.default)&&(t.Component=r.default),o.attachCmpt(t,e)})}else t.handler.call(this,e)},l.prototype.attachCmpt=function(t,r){var n=this,i=new t.Component;i.$router=this,i.data.set("route",r),"function"==typeof i.route&&i.route();var o=t.target,s=e(o);if(!s)throw new Error('[SAN-ROUTER ERROR] Attach failed, target element "'+t.target+'" is not found.');i.attach(s),this.routeAlives.push({component:i,id:t.id}),"function"==typeof t.handler&&setTimeout(function(){t.handler.call(n,r)})},l.prototype.add=function(t){var e=t.rule,r=[""];if("string"==typeof e){var n=e.replace(/\/:([a-z0-9_-]+)(?=\/|$)/gi,function(t,e){return r.push(e),"/([^/\\s]+)"});e=new RegExp("^"+n+"$","i")}if(!(e instanceof RegExp))throw new Error("[SAN-ROUTER ERROR] Rule must be string or RegExp!");var i=u();return this.routes.push({id:i,rule:e,handler:t.handler,keys:r,target:t.target||"#main",Component:t.Component,config:t}),this},l.prototype.push=function(t,e){(t=n(t))&&this.locator.redirect(t,e)};var v=new l,y={Link:{template:'<a href="{{hrefPrefix}}{{href}}" onclick="return false;" on-click="clicker($event)" target="{{target}}" class="{{isActive ? activeClass : \'\'}}"><slot/></a>',clicker:function(t){var e=this.data.get("href");"string"==typeof e&&v.locator.redirect(e.replace(/^#/,"")),t.preventDefault?t.preventDefault():t.returnValue=!1},inited:function(){var t=this;this.routeListener=function(e){t.data.set("isActive",e.url===t.data.get("href"))},this.routeListener({url:v.locator.current}),v.listen(this.routeListener)},disposed:function(){v.unlisten(this.routeListener),this.routeListener=null},initData:function(){return{isActive:!1,hrefPrefix:"hash"===v.mode?"#":""}},computed:{href:function(){return i(this.data.get("to")||"",v.locator.current)}}},router:v,Router:l,HashLocator:a,HTML5Locator:c,resolveURL:i,parseURL:r,stringifyURL:n,version:"1.2.4"};"object"==typeof exports&&"object"==typeof module?exports=module.exports=y:"function"==typeof define&&define.amd?define("san-router",[],y):t.sanRouter=y}(this);
!function(t){function e(t){switch(typeof t){case"object":return t;case"string":return document.querySelector?document.querySelector(t):document.getElementById(t.replace(/#/i,""))}}function r(t){var e={hash:"",queryString:"",params:{},query:{},path:t},r=e.path.indexOf("#");r>=0&&(e.hash=e.path.slice(r+1),e.path=e.path.slice(0,r));var n=e.query,o=e.path.indexOf("?");if(o>=0){e.queryString=e.path.slice(o+1),e.path=e.path.slice(0,o);for(var i=e.queryString.split("&"),s=0;s<i.length;s++){var a=i[s],u=a.indexOf("="),h="";u>0&&(h=a.slice(u+1),a=a.slice(0,u));var c=decodeURIComponent(a);h=decodeURIComponent(h),n.hasOwnProperty(c)?n[c]=[].concat(n[c],h):n[c]=h}}return e}function n(t){if(!t)return"";if("string"==typeof t)return t;var e,r=t.query,n=t.params,o=t.path||"",i=[];if(o)for(var s=o.split("/"),a=0,u=s.length;a<u;a++){var h=s[a];if(/^:[a-z0-9_-]+$/.test(h)){e=!0;var c=h.slice(1);i.push(n&&n[c]||r&&r[c])}else i.push(h)}var p=t.queryString||"";if(0===p.indexOf("?")&&(p=p.slice(1)),!p&&r&&(!e||n)){var f=!0;for(var l in r)r.hasOwnProperty(l)&&(p+=(f?"":"&")+l+"="+encodeURIComponent(r[l]),f=!1)}return i.join("/")+(p&&"?")+p}function o(t,e){var n=r(t),o=r(e),i=n.path;if(0===i.indexOf("/"))return t;var s;if(i){var a=i.split("/"),u=o.path.split("/");u.pop();for(var h=0;h<a.length;h++){var c=a[h];switch(c){case"..":u.pop();break;case".":break;default:u.push(c)}}""!==u[0]&&u.unshift(""),s=u.join("/")}else s=o.path;return s+(n.queryString?"?"+n.queryString:"")}function i(){}function s(){var t=location.href.indexOf("#");return t<0?"/":location.href.slice(t+1)||"/"}function a(){this.current=s(),this.referrer="";var t=this;this.hashChangeHandler=function(){t.redirect(s())}}function u(){return location.pathname+location.search}function h(){if(!h.isSupport)throw new Error("[SAN-ROUTER ERROR] Your navigator doesn't supports HTML5!");this.current=u(),this.referrer="";var t=this;this.popstateHandler=function(){t.referrer=t.current,t.current=u(),t.fire("redirect",{url:t.current,referrer:t.referrer})}}function c(){return(++_).toString()}function p(t){return t.prototype&&(5===t.prototype.nodeType||"san-cmpt"===t.prototype._type)}function f(t){t=t||{};var e=t.mode||"hash";this.routes=[],this.routeAlives=[],this.listeners=[],this.__withRouteListeners=[],this.__redirectListener=d(this),this.setMode(e)}function l(t,e){var r=e.rule,n=[""];if("string"==typeof r){var o=r.replace(/\/:([a-z0-9_-]+)(?=\/|$)/gi,function(t,e){return n.push(e),"/([^/\\s]+)"});r=new RegExp("^"+o+"$","i")}if(!(r instanceof RegExp))throw new Error("[SAN-ROUTER ERROR] Rule must be string or RegExp!");var i=c();t.routes.push({id:i,rule:r,handler:e.handler,keys:n,target:e.target||"#main",Component:e.Component,config:e})}function d(t){return function(e){function n(){if(h>0){if(u<t.listeners.length){t.listeners[u++].call(t,c)}else o();h>0&&n()}}function o(){if(h=-1,s)v(t,s);else for(var e=t.routeAlives.length;e--;)t.routeAlives[e].component.dispose(),t.routeAlives.splice(e,1)}var i=r(e.url),s=t.match(i,e.referrer),a=s?s.data:i,u=0,h=1,c={url:e.url,hash:a.hash,queryString:a.queryString,query:a.query,params:a.params,path:a.path,referrer:e.referrer,config:s&&s.route.config,data:s&&s.data,resume:function(){0===h&&(h=1,n())},suspend:function(){h=0},stop:function(){h=-1}};n()}}function v(t,e){for(var r=e.route,n=!1,o=t.routeAlives.length,i=t.__withRouteListeners.slice(0);o--;){var s=t.routeAlives[o];s.id===r.id?(s.component.data.set("route",e.data),"function"==typeof s.component.route&&s.component.route(),n=!0):(s.component.dispose(),t.routeAlives.splice(o,1))}n||(r.Component?p(r.Component)?y(t,e):r.Component().then(function(n){p(n)?r.Component=n:n.__esModule&&p(n.default)&&(r.Component=n.default),y(t,e)}):r.handler.call(t,e.data));for(var a=0,u=i.length;a<u;a++)i[a](e)}function y(t,r){var n=r.route,o=new n.Component;o.$router=t,o.data.set("route",r.data),"function"==typeof o.route&&o.route();var i=n.target,s=e(i);if(!s)throw new Error('[SAN-ROUTER ERROR] Attach failed, target element "'+n.target+'" is not found.');o.attach(s),t.routeAlives.push({component:o,id:n.id}),"function"==typeof n.handler&&setTimeout(function(){n.handler.call(t,r.data)})}function g(t){var e=new Function;e.prototype=t.prototype;var r=function(e){return t.call(this,e)||this};return r.prototype=new e,r.prototype.constructor=r,e.prototype.hasOwnProperty("aPack")&&(r.prototype.aPack=e.prototype.aPack),r}function m(t){var e;return e=0===t.toString().indexOf("class")?R(t):g(t),e.template=t.template,e.components=t.components,e.trimWhitespace=t.trimWhitespace,e.delimiters=t.delimiters,e.autoFillStyleAndId=t.autoFillStyleAndId,e.filters=t.filters,e.computed=t.computed,e.aPack=t.aPack,e.messages=t.messages,e}function w(t,e){e=e||S;var r,n,o;"function"==typeof t?(n=m(t),r=t.prototype,o=n.prototype):(r=t||{},n=Object.assign({},t),o=n);var i=r.inited;o.inited=function(){this.$router=e,this.data.set("route",e.match(e.locator.current,e.locator.referrer).data),"function"==typeof this.route&&this.route();var t=this;this.__routerListener=function(e){t.data.set("route",e.data),"function"==typeof t.route&&t.route()},e.__withRouteListeners.push(this.__routerListener),"function"==typeof i&&i.call(this)};var s=r.disposed;return o.disposed=function(){if(this.$router){for(var t=this.$router.__withRouteListeners,e=t.length;e--;)if(t[e]===this.__routerListener){t.splice(e,1);break}this.__routerListener=null,this.$router=null}"function"==typeof s&&s.call(this)},n}function L(t){return{template:'<a href="{{hrefPrefix}}{{href}}" onclick="return false;" on-click="clicker($event)" target="{{target}}" class="{{isActive ? activeClass : \'\'}}"><slot/></a>',clicker:function(e){var r=this.data.get("href");"string"==typeof r&&t.locator.redirect(r.replace(/^#/,"")),e.preventDefault?e.preventDefault():e.returnValue=!1},inited:function(){var e=this;this.routeListener=function(t){e.data.set("isActive",t.url===e.data.get("href"))},this.routeListener({url:t.locator.current}),t.listen(this.routeListener)},disposed:function(){t.unlisten(this.routeListener),this.routeListener=null},initData:function(){return{isActive:!1,hrefPrefix:"hash"===t.mode?"#":""}},computed:{href:function(){return o(this.data.get("to")||"",t.locator.current)}}}}i.prototype.on=function(t,e){"function"==typeof e&&(this._eventListeners||(this._eventListeners={}),this._eventListeners[t]||(this._eventListeners[t]=[]),this._eventListeners[t].push(e))},i.prototype.un=function(t,e){if(this._eventListeners&&this._eventListeners[t])if(e)for(var r=this._eventListeners[t],n=r.length;n--;)r[n]===e&&r.splice(n,1);else this._eventListeners[t]=[]},i.prototype.fire=function(t,e){if(!t)throw new Error("No event type specified");var r=this._eventListeners&&this._eventListeners[t];if(r)for(var n=0;n<r.length;n++)r[n](e)},a.prototype=new i,a.prototype.constructor=a,a.prototype.start=function(){window.addEventListener&&window.addEventListener("hashchange",this.hashChangeHandler,!1),window.attachEvent&&window.attachEvent("onhashchange",this.hashChangeHandler)},a.prototype.stop=function(){window.removeEventListener&&window.removeEventListener("hashchange",this.hashChangeHandler,!1),window.detachEvent&&window.detachEvent("onhashchange",this.hashChangeHandler)},a.prototype.redirect=function(t,e){e=e||{},t=o(t,this.current);var r=this.current,n=t!==r;n?(this.referrer=r,this.current=t,location.hash=t):r=this.referrer,!n&&!e.force||e.silent||this.fire("redirect",{url:t,referrer:r})},a.prototype.reload=function(){this.redirect(this.current,{force:!0})},h.prototype=new i,h.prototype.constructor=h,h.prototype.start=function(){window.addEventListener("popstate",this.popstateHandler)},h.prototype.stop=function(){window.removeEventListener("popstate",this.popstateHandler)},h.prototype.redirect=function(t,e){e=e||{},t=o(t,this.current);var r=this.current,n=t!==r;n&&(this.referrer=r,this.current=t,history.pushState({},"",t)),!n&&!e.force||e.silent||this.fire("redirect",{url:t,referrer:r})},h.prototype.reload=function(){this.fire("redirect",{url:this.current,referrer:this.referrer})},h.isSupport="pushState"in window.history;var _=365611;f.prototype.match=function(t,e){"string"==typeof t&&(t=r(t));for(var n=0;n<this.routes.length;n++){var o=this.routes[n],i=o.rule.exec(t.path);if(i){for(var s=o.keys||[],a=1;a<i.length;a++){var u=s[a]||a,h=i[a];t.query[u]=h,t.params[u]=h}return{data:{hash:t.hash,queryString:t.queryString,params:t.params,query:t.query,path:t.path,referrer:e},url:t,route:o}}}return null},f.prototype.listen=function(t){this.listeners.push(t)},f.prototype.unlisten=function(t){for(var e=this.listeners.length;e--;)this.listeners[e]===t&&this.listeners.splice(e,1)},f.prototype.start=function(){return this.isStarted||(this.isStarted=!0,this.locator.on("redirect",this.__redirectListener),this.locator.start(),this.locator.reload()),this},f.prototype.stop=function(){return this.locator.un("redirect",this.__redirectListener),this.locator.stop(),this.isStarted=!1,this},f.prototype.setMode=function(t){if(t=t.toLowerCase(),this.mode!==t){this.mode=t;var e=!1;switch(this.isStarted&&(this.stop(),e=!0),t){case"hash":this.locator=new a;break;case"html5":this.locator=new h}return e&&this.start(),this}},f.prototype.add=function(t){if(t instanceof Array)for(var e=0,r=t.length;e<r;e++)l(this,t[e]);else"object"==typeof t&&l(this,t);return this},f.prototype.push=function(t,e){(t=n(t))&&this.locator.redirect(t,e)};var R,S=new f;try{R=new Function("RawClass","return class extends RawClass {}")}catch(t){}var E={Link:L(S),createLink:L,withRoute:w,router:S,Router:f,HashLocator:a,HTML5Locator:h,resolveURL:o,parseURL:r,stringifyURL:n,version:"2.0.0"};"object"==typeof exports&&"object"==typeof module?exports=module.exports=E:"function"==typeof define&&define.amd?define("san-router",[],E):t.sanRouter=E}(this);

@@ -6,3 +6,3 @@ /**

(function (root) {
(function (root) {

@@ -104,3 +104,3 @@ /**

}
var query = source.query;

@@ -192,4 +192,4 @@ var params = source.params;

return path + (sourceLoc.queryString ? '?' + sourceLoc.queryString : '');

@@ -380,2 +380,6 @@ }

function HTML5Locator() {
if (!HTML5Locator.isSupport) {
throw new Error('[SAN-ROUTER ERROR] Your navigator doesn\'t supports HTML5!');
}
this.current = getLocation();

@@ -454,4 +458,2 @@ this.referrer = '';

var routeID = 0x5942b;

@@ -466,116 +468,3 @@ function guid() {

/**
* 获取 router 的 locator redirect 事件监听函数
*
* @return {Function}
*/
function getLocatorRedirectHandler(router) {
return function (e) {
var url = parseURL(e.url);
var routeItem;
for (var i = 0; i < router.routes.length; i++) {
var item = router.routes[i];
var match = item.rule.exec(url.path);
if (match) {
routeItem = item;
// fill params
var keys = item.keys || [];
for (var j = 1; j < match.length; j++) {
var key = keys[j] || j;
var value = match[j];
url.query[key] = value;
url.params[key] = value;
}
// fill referrer
url.referrer = e.referrer;
url.config = item.config;
break;
}
}
var i = 0;
var state = 1;
/**
* listener 事件对象
*
* @type {Object}
*/
var listenerEvent = {
url: e.url,
hash: url.hash,
queryString: url.queryString,
query: url.query,
params: url.params,
path: url.path,
referrer: url.referrer,
config: url.config,
resume: next,
suspend: function () {
state = 0;
},
stop: function () {
state = -1;
}
};
/**
* 尝试运行下一个listener
*
* @inner
*/
function doNext() {
if (state > 0) {
if (i < router.listeners.length) {
router.listeners[i].call(router, listenerEvent, url.config);
if (state > 0) {
next();
}
}
else {
routeAction();
}
}
}
/**
* 运行下一个listener
*
* @inner
*/
function next() {
state = 1;
i++;
doNext();
}
/**
* 运行路由行为
*
* @inner
*/
function routeAction() {
if (routeItem) {
router.doRoute(routeItem, url);
}
else {
var len = router.routeAlives.length;
while (len--) {
router.routeAlives[len].component.dispose();
router.routeAlives.splice(len, 1);
}
}
};
doNext();
};
}
/**

@@ -592,6 +481,2 @@ * 路由器类

if (mode === 'html5' && !HTML5Locator.isSupport) {
throw new Error('[SAN-ROUTER ERROR] Your navigator doesn\'t supports HTML5!');
}
this.routes = [];

@@ -601,4 +486,5 @@ this.routeAlives = [];

this.__withRouteListeners = [];
this.__redirectListener = routerGetRedirectListener(this);
this.locatorRedirectHandler = getLocatorRedirectHandler(this);
this.setMode(mode);

@@ -608,2 +494,45 @@ }

/**
* 匹配 url
*
* @param {Object|string} url 要匹配的url
* @return {Object} routeInfo
*/
Router.prototype.match = function (url, referrer) {
if (typeof url === 'string') {
url = parseURL(url);
}
for (var i = 0; i < this.routes.length; i++) {
var item = this.routes[i];
var match = item.rule.exec(url.path);
if (match) {
// fill params
var keys = item.keys || [];
for (var j = 1; j < match.length; j++) {
var key = keys[j] || j;
var value = match[j];
url.query[key] = value;
url.params[key] = value;
}
return {
data: {
hash: url.hash,
queryString: url.queryString,
params: url.params,
query: url.query,
path: url.path,
referrer: referrer
},
url: url,
route: item
};
}
}
return null;
}
/**
* 添加路由监听器

@@ -639,3 +568,3 @@ *

this.isStarted = true;
this.locator.on('redirect', this.locatorRedirectHandler);
this.locator.on('redirect', this.__redirectListener);
this.locator.start();

@@ -654,3 +583,3 @@ this.locator.reload();

Router.prototype.stop = function () {
this.locator.un('redirect', this.locatorRedirectHandler);
this.locator.un('redirect', this.__redirectListener);
this.locator.stop();

@@ -698,17 +627,183 @@ this.isStarted = false;

/**
* 添加路由项,支持单一数据或者数组配置
*
* @public
* @param {Object|Array<Object>} config 路由项配置
* @return {Object} san-router 实例
*/
Router.prototype.add = function (config) {
if (config instanceof Array) {
for (var i = 0, l = config.length; i < l; i++) {
routerAdd(this, config[i]);
}
}
else if (typeof config === 'object') {
routerAdd(this, config);
}
return this;
};
/**
* 编程式路由函数,间接使用 redirect 重定向,避免直接使用内部对象locator
*
* @param {Object|string} url 路由地址
* @param {Object?} options 重定向的行为配置
* @param {boolean?} options.force 是否强制刷新
*/
Router.prototype.push = function (url, options) {
url = stringifyURL(url);
if (url) {
this.locator.redirect(url, options);
}
};
/**
* 添加路由项
* 当规则匹配时,路由将优先将Component渲染到target中。如果没有包含Component,则执行handler函数
*
* @param {Object} config 路由项配置
* @param {string|RegExp} config.rule 路由规则
* @param {Function?} config.handler 路由函数
* @param {Function?} config.Component 路由组件
* @param {string} config.target 路由组件要渲染到的目标位置
*/
function routerAdd(router, config) {
var rule = config.rule;
var keys = [''];
if (typeof rule === 'string') {
// 没用path-to-regexp,暂时不提供这么多功能支持
var regText = rule.replace(
/\/:([a-z0-9_-]+)(?=\/|$)/ig,
function (match, key) {
keys.push(key);
return '/([^/\\s]+)';
}
);
rule = new RegExp('^' + regText + '$', 'i');
}
if (!(rule instanceof RegExp)) {
throw new Error('[SAN-ROUTER ERROR] Rule must be string or RegExp!');
}
var id = guid();
router.routes.push({
id: id,
rule: rule,
handler: config.handler,
keys: keys,
target: config.target || '#main',
Component: config.Component,
config: config
});
}
/**
* 获取 router 的 locator redirect 事件监听函数
*
* @param {Router} router router 实例
* @return {Function}
*/
function routerGetRedirectListener(router) {
return function (e) {
var url = parseURL(e.url);
var routeInfo = router.match(url, e.referrer);
var listenerSource = routeInfo ? routeInfo.data : url;
var i = 0;
var state = 1;
/**
* listener 事件对象
*
* @type {Object}
*/
var listenerEvent = {
url: e.url,
hash: listenerSource.hash,
queryString: listenerSource.queryString,
query: listenerSource.query,
params: listenerSource.params,
path: listenerSource.path,
referrer: e.referrer,
config: routeInfo && routeInfo.route.config,
data: routeInfo && routeInfo.data,
resume: function () {
if (state === 0) {
state = 1;
doNext();
}
},
suspend: function () {
state = 0;
},
stop: function () {
state = -1;
}
};
/**
* 尝试运行下一个listener
*
* @inner
*/
function doNext() {
if (state > 0) {
if (i < router.listeners.length) {
var listener = router.listeners[i++];
listener.call(router, listenerEvent);
}
else {
routeAction();
}
if (state > 0) {
doNext();
}
}
}
/**
* 运行路由行为
*
* @inner
*/
function routeAction() {
state = -1;
if (routeInfo) {
routerDoRoute(router, routeInfo);
}
else {
var len = router.routeAlives.length;
while (len--) {
router.routeAlives[len].component.dispose();
router.routeAlives.splice(len, 1);
}
}
}
doNext();
};
}
/**
* 执行路由
*
* @private
* @param {Object} routeItem 路由项
* @param {Object} e 路由信息
* @param {Object} routeInfo 路由信息
*/
Router.prototype.doRoute = function (routeItem, e) {
function routerDoRoute(router, routeInfo) {
var routeItem = routeInfo.route;
var isUpdateAlive = false;
var len = this.routeAlives.length;
var len = router.routeAlives.length;
var withRouteListeners = router.__withRouteListeners.slice(0);
while (len--) {
var routeAlive = this.routeAlives[len];
var routeAlive = router.routeAlives[len];
if (routeAlive.id === routeItem.id) {
routeAlive.component.data.set('route', e);
routeAlive.component.data.set('route', routeInfo.data);
if (typeof routeAlive.component.route === 'function') {

@@ -721,39 +816,41 @@ routeAlive.component.route();

routeAlive.component.dispose();
this.routeAlives.splice(len, 1);
router.routeAlives.splice(len, 1);
}
}
if (isUpdateAlive) {
return;
}
if (routeItem.Component) {
if (isComponent(routeItem.Component)) {
this.attachCmpt(routeItem, e);
if (!isUpdateAlive) {
if (routeItem.Component) {
if (isComponent(routeItem.Component)) {
routerAttachComponent(router, routeInfo);
}
else {
routeItem.Component().then(
function (Cmpt) { // eslint-disable-line
if (isComponent(Cmpt)) {
routeItem.Component = Cmpt;
}
else if (Cmpt.__esModule && isComponent(Cmpt['default'])) {
routeItem.Component = Cmpt['default'];
}
routerAttachComponent(router, routeInfo);
}
);
}
}
else {
var me = this;
routeItem.Component().then(
function (Cmpt) { // eslint-disable-line
if (isComponent(Cmpt)) {
routeItem.Component = Cmpt;
}
else if (Cmpt.__esModule && isComponent(Cmpt['default'])) {
routeItem.Component = Cmpt['default'];
}
me.attachCmpt(routeItem, e);
}
);
routeItem.handler.call(router, routeInfo.data);
}
}
else {
routeItem.handler.call(this, e);
for (var i = 0, l = withRouteListeners.length; i < l; i++) {
withRouteListeners[i](routeInfo);
}
};
}
Router.prototype.attachCmpt = function (routeItem, e) {
var me = this;
function routerAttachComponent(router, routeInfo) {
var routeItem = routeInfo.route;
var component = new routeItem.Component();
component['$router'] = this;
component.data.set('route', e);
component['$router'] = router;
component.data.set('route', routeInfo.data);
if (typeof component.route === 'function') {

@@ -763,3 +860,2 @@ component.route();

var target = routeItem.target;

@@ -777,3 +873,3 @@ var targetEl = elementSelector(target);

this.routeAlives.push({
router.routeAlives.push({
component: component,

@@ -786,75 +882,138 @@ id: routeItem.id

setTimeout(function () {
routeItem.handler.call(me, e);
})
routeItem.handler.call(router, routeInfo.data);
});
}
};
}
/**
* 添加路由项
* 当规则匹配时,路由将优先将Component渲染到target中。如果没有包含Component,则执行handler函数
*
* @private
* @param {Object} config 路由项配置
* @param {string|RegExp} config.rule 路由规则
* @param {Function?} config.handler 路由函数
* @param {Function?} config.Component 路由组件
* @param {string} config.target 路由组件要渲染到的目标位置
* @return {Object} san-router 实例
*/
Router.prototype.add = function (config) {
var rule = config.rule;
var keys = [''];
if (typeof rule === 'string') {
// 没用path-to-regexp,暂时不提供这么多功能支持
var regText = rule.replace(
/\/:([a-z0-9_-]+)(?=\/|$)/ig,
function (match, key) {
keys.push(key);
return '/([^/\\s]+)';
}
);
var router = new Router();
rule = new RegExp('^' + regText + '$', 'i');
var extendsAsClass;
try {
extendsAsClass = new Function('RawClass', "return class extends RawClass {}");
}
catch (ex) {}
function extendsAsFunc(RawClass) {
var F = new Function();
F.prototype = RawClass.prototype;
var NewClass = function (option) {
return RawClass.call(this, option) || this;
};
NewClass.prototype = new F();
NewClass.prototype.constructor = NewClass;
if (F.prototype.hasOwnProperty('aPack')) {
NewClass.prototype.aPack = F.prototype.aPack;
}
if (!(rule instanceof RegExp)) {
throw new Error('[SAN-ROUTER ERROR] Rule must be string or RegExp!');
return NewClass;
}
function extendsComponent(ComponentClass) {
var NewComponentClass;
if (ComponentClass.toString().indexOf('class') === 0) {
NewComponentClass = extendsAsClass(ComponentClass);
}
else {
NewComponentClass = extendsAsFunc(ComponentClass);
}
var id = guid();
this.routes.push({
id: id,
rule: rule,
handler: config.handler,
keys: keys,
target: config.target || '#main',
Component: config.Component,
config: config
});
NewComponentClass.template = ComponentClass.template;
NewComponentClass.components = ComponentClass.components;
NewComponentClass.trimWhitespace = ComponentClass.trimWhitespace;
NewComponentClass.delimiters = ComponentClass.delimiters;
NewComponentClass.autoFillStyleAndId = ComponentClass.autoFillStyleAndId;
NewComponentClass.filters = ComponentClass.filters;
NewComponentClass.computed = ComponentClass.computed;
NewComponentClass.aPack = ComponentClass.aPack;
NewComponentClass.messages = ComponentClass.messages;
return this;
};
return NewComponentClass;
}
/**
* 编程式路由函数,间接使用 redirect 重定向,避免直接使用内部对象locator
* 为组件生成支持路由关联的高阶组件
*
* @param {Object|string} url 路由地址
* @param {Object?} options 重定向的行为配置
* @param {boolean?} options.force 是否强制刷新
* @param {Function|Class} ComponentClass 组件类
* @param {Router?} customRouter 关联的 router 实例
* @returns 高阶组件
*/
Router.prototype.push = function (url, options) {
url = stringifyURL(url);
if (url) {
this.locator.redirect(url, options);
function withRoute(ComponentClass, customRouter) {
customRouter = customRouter || router;
var componentProto;
var ReturnTarget;
var extProto;
if (typeof ComponentClass === 'function') {
ReturnTarget = extendsComponent(ComponentClass);
componentProto = ComponentClass.prototype;
extProto = ReturnTarget.prototype;
}
else {
componentProto = ComponentClass || {};
ReturnTarget = Object.assign({}, ComponentClass);
extProto = ReturnTarget;
}
// 注入 $router 以及 data route
var inited = componentProto.inited;
extProto.inited = function () {
this.$router = customRouter;
this.data.set(
'route',
customRouter.match(
customRouter.locator.current,
customRouter.locator.referrer
).data
);
if (typeof this.route === 'function') {
this.route();
}
// 路由信息实时获取
var me = this;
this.__routerListener = function (e) {
me.data.set('route', e.data);
if (typeof me.route === 'function') {
me.route();
}
};
customRouter.__withRouteListeners.push(this.__routerListener);
if (typeof inited === 'function') {
inited.call(this);
}
};
// dispose 监听器
var disposed = componentProto.disposed;
extProto.disposed = function () {
if (this.$router) {
// unlisten from router
var listeners = this.$router.__withRouteListeners;
var len = listeners.length;
while (len--) {
if (listeners[len] === this.__routerListener) {
listeners.splice(len, 1);
break;
}
}
this.__routerListener = null;
this.$router = null;
}
if (typeof disposed === 'function') {
disposed.call(this);
}
};
return ReturnTarget;
}
var router = new Router();
var main = {
/**
* 路由链接的 San 组件
*/
Link: {
function createLink(router) {
return {
template: '<a href="{{hrefPrefix}}{{href}}" onclick="return false;" on-click="clicker($event)" '

@@ -906,4 +1065,9 @@ + 'target="{{target}}" class="{{isActive ? activeClass : \'\'}}"><slot/></a>',

}
},
};
}
var main = {
Link: createLink(router),
createLink: createLink,
withRoute: withRoute,
router: router,

@@ -917,6 +1081,6 @@ Router: Router,

version: '1.2.4'
version: '2.0.0'
};
if (typeof exports === 'object' && typeof module === 'object') {

@@ -934,3 +1098,2 @@ // For CommonJS

})(this);
})(this);
{
"name": "san-router",
"version": "1.2.4",
"version": "2.0.0",
"description": "Router for San",
"scripts": {
"pretest": "npm run build && http-server ./ &",
"test": "open http://127.0.0.1:8080/test/",
"build": "rm -rf dist && mkdir dist && uglifyjs index.js -mco dist/san-router.js"
"pretest": "npm run build",
"test": "http-server -c-1 -o /test",
"build": "rm -rf dist && mkdir dist && uglifyjs index.js -mco dist/san-router.js",
"docs:start": "docusaurus start --config ./website/docusaurus.config.js",
"docs:build": "docusaurus build --config ./website/docusaurus.config.js"
},
"devDependencies": {
"@docusaurus/core": "^2.0.0-beta.18",
"@docusaurus/preset-classic": "^2.0.0-beta.18",
"http-server": "^0.12.3",
"jasmine-core": "^2.99.1",
"san": "^3.4.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"san": "^3.11.2",
"uglify-js": "^2.7.5"

@@ -24,3 +30,2 @@ },

"types": "types",
"dependencies": {},
"author": "otakustay",

@@ -27,0 +32,0 @@ "repository": {

@@ -6,278 +6,47 @@ # san-router

[San](https://baidu.github.io/san/) 框架的官方 router。通常,单页或同构的 Web 应用都会需要一个 router。
[San](https://baidu.github.io/san/) 框架的官方 router,支持动态路由,嵌套路由,路由懒加载以及导航守卫等功能。
你可以从下面找到 san-router 的下载和使用说明,也可以直接从 [示例项目](https://github.com/baidu/san/tree/master/example/todos-esnext) 看看实际项目中的使用方法。
> 注意:使用 san-router,要求 San 的版本号 >= 3.0.2
[文档](https://baidu.github.io/san-router/) | [示例项目](https://github.com/baidu/san/tree/master/example/todos-esnext)
下载
----
## 下载
NPM:
```
$ npm i san-router
# use npm
npm i san-router
# or use yarn
yarn add san-router
```
## 使用
使用
----
如需了解更多功能,请访问 [快速开始](https://baidu.github.io/san-router/docs/quick-start)
### Webpack + Babel
通过 named import 导入
```javascript
```
import {router, Link} from 'san-router';
router.add({
rule: '/book/:id',
rule: '/book',
Component: BookDetail
});
router.add([
{
rule: '/about',
Component: About
},
{
rule: '/home',
Component: Home
}
]);
router.start();
```
webpack 环境配置网上有太多文章,在此不赘述了
## CHANGELOG
[CHANGELOG](https://github.com/baidu/san-router/blob/master/CHANGELOG.md)
### AMD
## License
通过 require 拿到的 exports 上包含 router 和 Link
```javascript
var sanRouter = require('san-router');
var router = sanRouter.router;
var Link = sanRouter.Link;
router.add({
rule: '/book/:id',
Component: BookDetail
});
router.start();
```
请为 amd loader 正确配置 san-router 的引用路径。通过 npm 安装的项目可以采用下面的配置
```javascript
require.config({
baseUrl: 'src',
paths: {
'san-router': '../dep/san-router/dist/san-router.source'
}
});
```
API
----
### router
路由器对象。
路由用于将特定的 URL 对应到组件类上。在 URL 变化并匹配路由规则时,路由将初始化特定组件并渲染到页面上。你也可以将 URL 对应到函数上,路由将在 URL 规则匹配时执行特定函数。
#### add({Object}options)
`说明`
添加一条路由规则。
你可以指定 Component 和 target 参数,让规则匹配时在 target 中渲染一个 Component。
```javascript
router.add({
rule: '/book/:id',
Component: san.defineComponent({
route() {
let route = this.data.get('route');
// route.query.id
}
}),
target: '.app-main'
});
```
Component 属性也可以用来加载一个异步的组件:
```js
const BookComponent = () => import('./BookComponent');
router.add({
rule: '/book/:id',
Component: BookComponent,
target: '.app-main'
});
```
你也可以指定一个 handler 函数,让规则匹配时执行这个函数。
```javascript
router.add({
rule: '/book/:id',
handler(e) {
e.query.id
}
});
```
路由规则有2种形式:
- `string` URL 的 path 部分与字符串完全匹配。支持 `:` 标记的路径参数,根据名称作为 query 的参数传递给相应的组件或函数。
- `RegExp` URL 的 path 部分匹配该正则。正则中的 group 将根据序号作为 query 的参数传递给相应的组件或函数。
`参数`
- `string|RegExp` options.rule - 路由规则
- `Function` options.Component - 规则匹配时渲染的组件类
- `string` options.target - 组件渲染的容器元素,默认值为 **#main**
- `Function` options.handler 规则匹配时的执行函数
#### listen({Function}listener)
`说明`
添加路由监听器。当发生路由行为时,监听器函数被触发。
```javascript
router.listen(function (e, config) {
e.query.id
});
```
`参数`
- `Function` listener - 监听器函数
`监听器函数参数`
- `Object` e - 路由信息对象
- `Object` config - 路由配置对象
路由监听器作为所有路由的切面函数,通常承担权限判断之类基础的任务。所以路由监听器可以通过 `stop` 方法阻断当前路由过程,并进行 URL 跳转。
```javascript
router.listen(function (e) {
if (!checkPermission()) {
e.stop();
this.locator.redirect('/forbidden');
}
});
```
路由监听器可以通过 `suspend` 和 `resume` 方法中断和唤醒路由过程,实现异步。不过异步过程会导致路由对应的视图渲染延迟,慎用。
```javascript
router.listen(function (e) {
e.suspend();
checkPermission().then(invalid => {
if (invalid) {
e.stop();
this.locator.redirect('/forbidden');
return;
}
e.resume();
});
});
```
#### unlisten({Function}listener)
`说明`
移除路由监听器。
`参数`
- `Function` listener - 监听器函数
#### setMode({string}mode)
`说明`
设置路由模式。支持两种模式:hash | html5,默认为 hash。
`参数`
- `string` mode - 路由模式,hash | html5
### push({Object}url[,{Object}options])
`说明`
功能类似 router.locator.redict 方法,并增加了 query 对象的处理。同时在 san 组件中可通过实例的 `$router` 方法访问当前路由实例
```js
router.push({
query: {
name: 'erik',
sex: 1,
age: 18
}
});
// san 组件中
this.$router.push({
query: {
name: 'erik',
sex: 1,
age: 18
}
});
```
#### start()
`说明`
启动路由
#### stop()
`说明`
停止路由
### Link
具有路由功能的应用中,建议使用 Link 组件代替 a 标签。Link 组件可以根据路由模式渲染一个链接。通过 to 属性指定目标地址,Link 组件将渲染一个带有正确地址的 a 标签。
```javascript
san.defineComponent({
components: {
'router-link': Link
},
template: `
<div>
<header>
<router-link to="/user">用户信息</router-link>
</header>
</div>
`
});
```
## ChangeLogs
### 1.2.4
- [feature] `router` 增加 `push` 方法
- [feature] 在 san 组件中通过 `this.$router` 可以访问 router 实例
- [fix] 修复 HashLocator 原型指向问题
### 1.2.3
- [fix] 修复在 san 3.10.2 版本不支持 callHook 的问题,不再依赖此实现。
san-router is [MIT licensed](./LICENSE).
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc