Backbone.History和Backbone.Router

history和router都是控制路由的,做一个单页应用,要控制前进后退,就可以用到他们了。

History类用于监听URL的变化,和触发Action方法,他可以添加对url的监听,

Router类用于定义和解析路由规则,并将URL映射到Action。

router和history一些个人的注解

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8"/>
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
  6. <title>backbone</title>
  7. <style type="text/css">
  8. *{padding:0;margin:0;}
  9. .wrap{width:960px; margin: 100px auto; padding: 20px 0;}
  10. ul{ list-style: none;}
  11. </style>
  12. </head>
  13. <body>
  14. <div class="wrap">
  15. <div id="a1"></div>
  16. <div id="a2"></div>
  17. <div id="a3"></div>
  18. </div>
  19. <script src="http://files.cnblogs.com/wtcsy/jquery.js"></script>
  20. <script src="http://files.cnblogs.com/wtcsy/underscore.js"></script>
  21. <script src="http://files.cnblogs.com/wtcsy/events.js"></script>
  22. <script>
  23. (function(){
  24. // Backbone.History
  25. // ----------------
  26.  
  27. // Cached regex for stripping a leading hash/slash and trailing space.
  28. var routeStripper = /^[#\/]|\s+$/g;
  29.  
  30. // Cached regex for stripping leading and trailing slashes.
  31. var rootStripper = /^\/+|\/+$/g;
  32.  
  33. // Cached regex for stripping urls of hash.
  34. var pathStripper = /#.*$/;
  35.  
  36. // Handles cross-browser history management, based on either
  37. // [pushState](http://diveintohtml5.info/history.html) and real URLs, or
  38. // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
  39. // and URL fragments. If the browser supports neither (old IE, natch),
  40. // falls back to polling.
  41. var History = Backbone.History = function() {
  42. this.handlers = [];
  43. _.bindAll(this, 'checkUrl');
  44.  
  45. // Ensure that `History` can be used outside of the browser.
  46. if (typeof window !== 'undefined') {
  47. this.location = window.location;
  48. this.history = window.history;
  49. }
  50. };
  51.  
  52. // Has the history handling already been started?
  53. History.started = false;
  54.  
  55. _.extend(History.prototype, Backbone.Events, {
  56.  
  57. // The default interval to poll for hash changes, if necessary, is
  58. // twenty times a second.
  59. interval: 50,
  60.  
  61. // Are we at the app root?
  62. atRoot: function() {
  63. var path = this.location.pathname.replace(/[^\/]$/, '$&/');
  64. return path === this.root && !this.location.search;
  65. },
  66.  
  67. // Gets the true hash value. Cannot use location.hash directly due to bug
  68. // in Firefox where location.hash will always be decoded.
  69. getHash: function(window) {
  70. var match = (window || this).location.href.match(/#(.*)$/);
  71. return match ? match[1] : '';
  72. },
  73.  
  74. // Get the pathname and search params, without the root.
  75. getPath: function() {
  76. var path = decodeURI(this.location.pathname + this.location.search);
  77. var root = this.root.slice(0, -1);
  78. if (!path.indexOf(root)) path = path.slice(root.length);
  79. return path.slice(1);
  80. },
  81.  
  82. // Get the cross-browser normalized URL fragment from the path or hash.
  83. getFragment: function(fragment) {
  84. if (fragment == null) {
  85. if (this._hasPushState || !this._wantsHashChange) {
  86. fragment = this.getPath();
  87. } else {
  88. fragment = this.getHash();
  89. }
  90. }
  91. //var routeStripper = /^[#\/]|\s+$/g;
  92. return fragment.replace(routeStripper, '');
  93. },
  94. // Start the hash change handling, returning `true` if the current URL matches
  95. // an existing route, and `false` otherwise.
  96. start: function(options) {
  97. if (History.started) throw new Error("Backbone.history has already been started");
  98. History.started = true;
  99.  
  100. // Figure out the initial configuration. Do we need an iframe?
  101. // Is pushState desired ... is it available?
  102. this.options = _.extend({root: '/'}, this.options, options);
  103. this.root = this.options.root;
  104. this._wantsHashChange = this.options.hashChange !== false;
  105. this._hasHashChange = 'onhashchange' in window;
  106. this._wantsPushState = !!this.options.pushState;
  107. this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState);
  108. this.fragment = this.getFragment();
  109.  
  110. // Add a cross-platform `addEventListener` shim for older browsers.
  111. var addEventListener = window.addEventListener || function (eventName, listener) {
  112. return attachEvent('on' + eventName, listener);
  113. };
  114.  
  115. // Normalize root to always include a leading and trailing slash.
  116. // var routeStripper = /^[#\/]|\s+$/g;
  117. this.root = ('/' + this.root + '/').replace(rootStripper, '/');
  118.  
  119. // Proxy an iframe to handle location events if the browser doesn't
  120. // support the `hashchange` event, HTML5 history, or the user wants
  121. // `hashChange` but not `pushState`.
  122. if (!this._hasHashChange && this._wantsHashChange && (!this._wantsPushState || !this._hasPushState)) {
  123. var iframe = document.createElement('iframe');
  124. iframe.src = 'javascript:0';
  125. iframe.style.display = 'none';
  126. iframe.tabIndex = -1;
  127. var body = document.body;
  128. // Using `appendChild` will throw on IE < 9 if the document is not ready.
  129. this.iframe = body.insertBefore(iframe, body.firstChild).contentWindow;
  130. this.navigate(this.fragment);
  131. }
  132.  
  133. // Depending on whether we're using pushState or hashes, and whether
  134. // 'onhashchange' is supported, determine how we check the URL state.
  135. if (this._hasPushState) {
  136. addEventListener('popstate', this.checkUrl, false);
  137. } else if (this._wantsHashChange && this._hasHashChange && !this.iframe) {
  138. addEventListener('hashchange', this.checkUrl, false);
  139. } else if (this._wantsHashChange) {
  140. this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
  141. }
  142. },
  143.  
  144. // Add a route to be tested when the fragment changes. Routes added later
  145. // may override previous routes.
  146. route: function(route, callback) {
  147. this.handlers.unshift({route: route, callback: callback});
  148. },
  149.  
  150. // Checks the current URL to see if it has changed, and if it has,
  151. // calls `loadUrl`, normalizing across the hidden iframe.
  152. checkUrl: function(e) {
  153. var current = this.getFragment();
  154. if (current === this.fragment && this.iframe) {
  155. current = this.getHash(this.iframe);
  156. }
  157. if (current === this.fragment) return false;
  158. if (this.iframe) this.navigate(current);
  159. this.loadUrl();
  160. },
  161. // Attempt to load the current URL fragment. If a route succeeds with a
  162. // match, returns `true`. If no defined routes matches the fragment,
  163. // returns `false`.
  164. loadUrl: function(fragment) {
  165. fragment = this.fragment = this.getFragment(fragment);
  166. return _.any(this.handlers, function(handler) {
  167. if (handler.route.test(fragment)) {
  168. handler.callback(fragment);
  169. return true;
  170. }
  171. });
  172. },
  173. // Save a fragment into the hash history, or replace the URL state if the
  174. // 'replace' option is passed. You are responsible for properly URL-encoding
  175. // the fragment in advance.
  176. //
  177. // The options object can contain `trigger: true` if you wish to have the
  178. // route callback be fired (not usually desirable), or `replace: true`, if
  179. // you wish to modify the current URL without adding an entry to the history.
  180. navigate: function(fragment, options) {
  181. if (!History.started) return false;
  182. if (!options || options === true) options = {trigger: !!options};
  183.  
  184. var url = this.root + (fragment = this.getFragment(fragment || ''));
  185.  
  186. // Strip the hash for matching.
  187. // var pathStripper = /#.*$/;
  188. fragment = fragment.replace(pathStripper, '');
  189.  
  190. if (this.fragment === fragment) return;
  191. this.fragment = fragment;
  192.  
  193. // Don't include a trailing slash on the root.
  194. if (fragment === '' && url !== '/') url = url.slice(0, -1);
  195.  
  196. // If pushState is available, we use it to set the fragment as a real URL.
  197. if (this._hasPushState) {
  198. this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
  199.  
  200. // If hash changes haven't been explicitly disabled, update the hash
  201. // fragment to store history.
  202. } else if (this._wantsHashChange) {
  203. this._updateHash(this.location, fragment, options.replace);
  204. if (this.iframe && (fragment !== this.getHash(this.iframe))) {
  205. // Opening and closing the iframe tricks IE7 and earlier to push a
  206. // history entry on hash-tag change. When replace is true, we don't
  207. // want this.
  208. if(!options.replace) this.iframe.document.open().close();
  209. this._updateHash(this.iframe.location, fragment, options.replace);
  210. }
  211.  
  212. // If you've told us that you explicitly don't want fallback hashchange-
  213. // based history, then `navigate` becomes a page refresh.
  214. } else {
  215. return this.location.assign(url);
  216. }
  217. if (options.trigger) return this.loadUrl(fragment);
  218. },
  219. // Update the hash location, either replacing the current entry, or adding
  220. // a new one to the browser history.
  221. _updateHash: function(location, fragment, replace) {
  222. if (replace) {
  223. var href = location.href.replace(/(javascript:|#).*$/, '');
  224. location.replace(href + '#' + fragment);
  225. } else {
  226. // Some browsers require that `hash` contains a leading #.
  227. location.hash = '#' + fragment;
  228. }
  229. }
  230. });
  231.  
  232. Backbone.history = new History;
  233. //Backbone.history.start()
  234. //Backbone.history.navigate
  235.  
  236. // Backbone.Router
  237. // ---------------
  238.  
  239. // Routers map faux-URLs to actions, and fire events when routes are
  240. // matched. Creating a new one sets its `routes` hash, if not set statically.
  241. var Router = Backbone.Router = function(options) {
  242. options || (options = {});
  243. if (options.routes) this.routes = options.routes;
  244. this._bindRoutes();
  245. this.initialize.apply(this, arguments);
  246. };
  247.  
  248. // Cached regular expressions for matching named param parts and splatted
  249. // parts of route strings.
  250. var optionalParam = /\((.*?)\)/g;
  251. var namedParam = /(\(\?)?:\w+/g;
  252. var splatParam = /\*\w+/g;
  253. var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
  254.  
  255. // Set up all inheritable **Backbone.Router** properties and methods.
  256. _.extend(Router.prototype, Backbone.Events, {
  257. // Initialize is an empty function by default. Override it with your own
  258. // initialization logic.
  259. initialize: function(){},
  260. // Manually bind a single named route to a callback. For example:
  261. //
  262. // this.route('search/:query/p:num', 'search', function(query, num) {
  263. // ...
  264. // });
  265. //
  266. route: function(route, name, callback) {
  267. if (!_.isRegExp(route)) route = this._routeToRegExp(route);
  268. if (_.isFunction(name)) {
  269. callback = name;
  270. name = '';
  271. }
  272. if (!callback) callback = this[name];
  273. var router = this;
  274. Backbone.history.route(route, function(fragment) {
  275. var args = router._extractParameters(route, fragment);
  276. if (router.execute(callback, args, name) !== false) {
  277. router.trigger.apply(router, ['route:' + name].concat(args));
  278. router.trigger('route', name, args);
  279. Backbone.history.trigger('route', router, name, args);
  280. }
  281. });
  282. return this;
  283. },
  284. // Convert a route string into a regular expression, suitable for matching
  285. // against the current location hash.
  286. _routeToRegExp: function(route) {
  287. route = route.replace(escapeRegExp, '\\$&') //把正则里面需要转移的字符进行转移
  288. .replace(optionalParam, '(?:$1)?') //把捕获变成非捕获 并且变成惰性匹配
  289. .replace(namedParam, function(match, optional) {
  290. return optional ? match : '([^/?]+)';
  291. }) //如果是:\w+格式转化成([^/?]+) 如果是非捕获格式(?: 则不进行转换
  292. .replace(splatParam, '([^?]*?)'); //把这种*\w+格式替换成 ([^?]*?)
  293. return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
  294. },
  295.  
  296. // Simple proxy to `Backbone.history` to save a fragment into the history.
  297. navigate: function(fragment, options) {
  298. Backbone.history.navigate(fragment, options);
  299. return this;
  300. },
  301. // Execute a route handler with the provided parameters. This is an
  302. // excellent place to do pre-route setup or post-route cleanup.
  303. execute: function(callback, args, name) {
  304. if (callback) callback.apply(this, args);
  305. },
  306. // Bind all defined routes to `Backbone.history`. We have to reverse the
  307. // order of the routes here to support behavior where the most general
  308. // routes can be defined at the bottom of the route map.
  309. _bindRoutes: function() {
  310. if (!this.routes) return;
  311. this.routes = _.result(this, 'routes');
  312. var route, routes = _.keys(this.routes);
  313. while ((route = routes.pop()) != null) {
  314. this.route(route, this.routes[route]);
  315. }
  316. },
  317.  
  318. // Given a route, and a URL fragment that it matches, return the array of
  319. // extracted decoded parameters. Empty or unmatched parameters will be
  320. // treated as `null` to normalize cross-browser behavior.
  321. _extractParameters: function(route, fragment) {
  322. var params = route.exec(fragment).slice(1);
  323. return _.map(params, function(param, i) {
  324. // Don't decode the search params.
  325. if (i === params.length - 1) return param || null;
  326. return param ? decodeURIComponent(param) : null;
  327. });
  328. }
  329.  
  330. });
  331.  
  332. Backbone.Router.extend = function(protoProps, staticProps) {
  333. var parent = this;
  334. var child;
  335.  
  336. // The constructor function for the new subclass is either defined by you
  337. // (the "constructor" property in your `extend` definition), or defaulted
  338. // by us to simply call the parent's constructor.
  339. if (protoProps && _.has(protoProps, 'constructor')) {
  340. child = protoProps.constructor;
  341. } else {
  342. child = function(){ return parent.apply(this, arguments); };
  343. }
  344.  
  345. // Add static properties to the constructor function, if supplied.
  346. //将静态方法和 parent上的静态方法一起扩展到child上面去
  347. _.extend(child, parent, staticProps);
  348.  
  349. // Set the prototype chain to inherit from `parent`, without calling
  350. // `parent`'s constructor function.
  351. //创建一个新的构造含糊Surrogate ;
  352. //this.constructor = child的意思是 Surrogate实例化后的对象 让对象的构造函数指向child
  353. // Surrogate的原型就是parent的原型
  354. // 然后实例化给child的原型,
  355. // 这里不是直接从new parent给child.prototype 而是创建一个新的构造函数,我也不知道为啥要这样
  356. var Surrogate = function(){ this.constructor = child; };
  357. Surrogate.prototype = parent.prototype;
  358. child.prototype = new Surrogate;
  359.  
  360. // Add prototype properties (instance properties) to the subclass,
  361. // if supplied.
  362. // 把第一个参数上的属性扩展到child.prototype
  363. if (protoProps) _.extend(child.prototype, protoProps);
  364.  
  365. // Set a convenience property in case the parent's prototype is needed
  366. // later.
  367. // 拿一个属性引用父的原型, 以免以后要用到.
  368. child.__super__ = parent.prototype;
  369.  
  370. return child;
  371. }
  372. })();
  373. </script>
  374. </body>
  375. </html>

history怎么实现一个页面里面让浏览器前进后退了?用到了以下的方法

onhashchange   pushstate

onhashchange  给window绑定onhashchange事件,当描点变化的时候,触发事件,然后就可以改变页面了

但onhashchange是从ie8开始能够支持,如果要做le6,7的兼容则必须用其他的办法,backbone的实现是创建一个隐藏的iframe,同时改变浏览器的url上的锚点和iframe的锚点,用一个定时器不停的监听浏览器url的变化

pushstate,html5里面新方法,给window绑定onpopstate方法,查看history.state的值,如果自己通过history.pushState改变了history.state的值,就当做该url之前已经存在,如果history.state是undefined,表示是新地址,要用history.pushState加到history里面去。

下面3个小demo分别是实现用onhashchange,pushstate和隐藏iframe实现浏览器前进和后退

onhashchange方式

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8"/>
  5. <title>Lottery Demo</title>
  6. <style type="text/css">
  7. *{padding:0;margin:0;}
  8.  
  9. </style>
  10. </head>
  11. <body>
  12. <div id="wrap"></div>
  13. <script type="text/javascript" src="jquery.js"></script>
  14. <script type="text/javascript">
  15.  
  16. var urlHash = {
  17. "one" : "我是第一页",
  18. "two" : "我是第二页",
  19. "three" : "我是第三页"
  20. }
  21. function c(){
  22. var hash = location.hash.replace("#","");
  23. if(hash in urlHash){
  24. $("#wrap").html(urlHash[hash]);
  25. }else{
  26. $("#wrap").html("该锚点没有对应的页面");
  27. }
  28. }
  29. function n(fragment){
  30. location.hash = fragment;
  31. }
  32. window.attachEvent
  33. ? window.attachEvent('onhashchange', c)
  34. : window.addEventListener("hashchange",c,false);
  35. n("one");
  36. //用n函数跳转描点
  37. </script>
  38. </body>
  39. </html>

pushstate

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8"/>
  5. <title>Lottery Demo</title>
  6. <style type="text/css">
  7. *{padding:0;margin:0;}
  8.  
  9. </style>
  10. </head>
  11. <body>
  12. <div id="wrap"></div>
  13. <script type="text/javascript" src="jquery.js"></script>
  14. <script type="text/javascript">
  15.  
  16. var urlHash = {
  17. "one" : "我是第一页",
  18. "two" : "我是第二页",
  19. "three" : "我是第三页"
  20. }
  21. function n(fragment){
  22. //history.pushState({lk:fragment,text:fragment}, "", fragment);
  23. var text =urlHash[fragment] || "没有对应的页面";
  24. history.pushState({lk:fragment,text:text}, "", fragment);
  25. $("#wrap").html(history.state.text);
  26. }
  27. window.addEventListener("popstate",function(){
  28. if(history.state){
  29. $("#wrap").html(history.state.text);
  30. }
  31. },false);
  32. n("one");
  33. //用n函数跳转描点
  34. </script>
  35. </body>
  36. </html>

用iframe的方式

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8"/>
  5. <title>Lottery Demo</title>
  6. <style type="text/css">
  7. *{padding:0;margin:0;}
  8.  
  9. </style>
  10. </head>
  11. <body>
  12. <div id="wrap"></div>
  13. <script type="text/javascript" src="jquery.js"></script>
  14. <script type="text/javascript">
  15.  
  16. var urlHash = {
  17. "one" : "我是第一页",
  18. "two" : "我是第二页",
  19. "three" : "我是第三页"
  20. }
  21.  
  22. var iframe = document.createElement('iframe')
  23. iframe.src = 'javascript:0';
  24. iframe.style.display = 'none';
  25. iframe.tabIndex = -1;
  26. var iframeWindow = document.body.insertBefore(iframe, document.body.firstChild).contentWindow;
  27. var currFragment = null;
  28.  
  29. function n(fragment){
  30. if(currFragment == fragment || fragment=="")return;
  31. currFragment = fragment;
  32. window.location.hash = '#' + fragment;
  33. window.iframeWindow.document.open().close();
  34. window.iframeWindow.location.hash = '#' + fragment;
  35. var text =urlHash[fragment] || "没有对应的页面";
  36. $("#wrap").html(text);
  37. }
  38.  
  39. setInterval(function(){
  40. var fragment = window.location.hash.replace("#","")
  41. n(fragment)
  42. }, 50);
  43. //用n函数跳转描点
  44. </script>
  45. </body>
  46. </html>

Backbone.History

Backbone.History是一个类,Backbone.history才是Backbone.History实例化后的对象,即Backbone.history = new Backbone.History;

Backbone.History有以下比较重要的方法

start

要启动Backbone的路由功能,首先必须调用Backbone.history.start(),start方法做了以下一些事情

首页设置 History.started=true 表示该路由功能已经启动

然后看当前浏览器是否支持onhashChange,参数入的参数是希望通过hash的方式还是希望通过pushstate的方式改变url

根据传入的root来设置url路径将怎么改变,默认以当前的路径

如果浏览器不支持onhashchange(不支持hashchange肯定也不会支持pushstate的,因为onhashchange出现的时候,还没有出现pushstate),创建一个iframe

如果是pushstate方式 绑定window的popstate事件去监听url改变

如果是hashchange方式 绑定window的hashchange事件去监听url改变

如果不支持onhashchange,则设置一个定时器为监听url的改变

  1. var r = Backbone.history;
  2. r.navigate("one",true);//这里锚点是不会改变的 因为没有调用start方法
  3. r.start();
  4. r.navigate("one",true);//锚点可以改变了

getFragment

获取当前的锚点或者是地址(地址,暂且这么说吧,因为pushstate方式就是后面那串地址不同)

route    (reg,callback)

添加对路由的监听事件,第一个参数是个正则,第2个参数是回调函数。以{route: route, callback: callback}的形式存在this.handlers中,当路由改变的时候,会遍历this.handlers,如果符合其中的route正则,则会执行相关回调

  1. var r = Backbone.history;
  2. r.route(/^a/,function(){alert("a")}); //监听描点如果是以a开头,则弹出a
  3. r.route(/^[^a]/,function(){alert("not a")}) //监听描点如果不是以a开头,则弹出not a
  4. r.start()
  5. r.navigate("aasd/asdf/ss",true)
  6. r.navigate("bxx/asdf/ss",true)

navigate  (fragment, [options])

第一个参数个地址,需要进入的地址,第2个参数可以是对象,也可以是布尔值,true,表示路由要改变,如果有对应的回调事件也要执行,false表示不需要执行对应的回调事件,第2个参数当是对象的时候,trigger值得意义和布尔值的时候一样,replace值表示修改路由,但是不触发相应的回调事件,replace是不会记录在history里面,返回的时候不会有替换之前的那个地址.在然后改变url地址,设置了pushstate的用pushstate方式,支持onhashchang的直接用location.href来改变描点,不支持onhashchange的,除了改变location.href还要改变iframe的location.href。最后在触发相关监听路由的回调事件

在调用navigate方法的时候就会调用一次回调事件了,监听的时候也会调用一次回调事件的,所以在监听的时候会判断是不是同一个路由,同一个就不执行回调了,监听执行额回调事件也是在浏览器的前进和后退的时候在会执行,只用调用navigate,监听是不会执行回调的,是navigate方法里面主动调用的

一些例子

要启用路由功能一定要先调用start

  1. var r = Backbone.history;
  2. r.start();
  3. r.navigate("aaa",true)
  4. //r.navigate("aaa",{trigger:true}) 这种方式和上面那种方式是一样的

第二个参数如果设置成false,路由会改变,但是监听函数是不会触发的

  1. var r = Backbone.history;
  2. r.start();
  3. r.route(/^a/,function(){alert("a")})
  4. r.route(/^b/,function(){alert("b")})
  5. r.navigate("aaaa",true) //可以触发监听回调
  6. r.navigate("aaaa",false) //触发不了监听回调

如果第二个参数是一个对象,且设置了replace为true,改变路由后,之前的路由是不会记录在history里面的,而且监听路由的回调也不会执行

  1. var r = Backbone.history;
  2. r.start();
  3. r.route(/a^/,function(){alert(1)})
  4. r.navigate("as",{trigger:true,replace:true}) //监听路由的回调是不会执行的,且history中不会记录之前的路由

所以第二个参数false和replace的却别就在于histroy中是否记录之前的路由,他们的相同点是都不会触发监听回调

Backbone.Router

Backbone.Router基本上对Backbone.history一些方法的封装了.

route router.route(route, name, [callback])

route方法就是添加路由,路由对应的回调,他有3个参数,第一个参数就是路由了,第2个参数是方法名,如果传递了,当匹配域名的时候就会从Router这个对象里面去掉用该方法名,第3个参数也是回调,不设置第2个参数的时候就会调用第3个参数的回调

route的执行过程如下,首先判断route参数是不是正则表达式,如果不是则转成正则表达式,转换过程下

首先,把字符串route里面需要转义的字符全部转义,转义规则如下

  1. var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
  2. route = route.replace(escapeRegExp, '\\$&');

需要转义的字符有\,-,{,},[,],+,.,\,?

然后把捕获变成非捕获,并且变成惰性匹配,因为这个正则后面要用到的是test方法,根本就不需要捕获,捕获会耗去更多的内存.

  1. var optionalParam = /\((.*?)\)/g;
  2. route = route.replace(optionalParam, '(?:$1)?')

然后在把 :\w+格式 转化成([^/?]+),路由上面的传参数的格式/:xxx,这个主要是把参数转换成([^/?]+),这个这则的意思就是不能是/和?的任意字符,因为参数的规则就是route/:arguments,如果带了/可能就路径错了,如果有?那就可能是url的参数

  1. var namedParam = /(\(\?)?:\w+/g;
  2. route = route.replace(namedParam, function(match, optional) {
  3. return optional ? match : '([^/?]+)';
  4. })

在然后 把点*以及*后面的字符串转化成([^?]*?),惰性匹配非?字符串 这个替换是干嘛用的还没看懂

  1. var splatParam = /\*\w+/g;
  2. route = route.replace(splatParam, '([^?]*?)');

最后给route字符串加上开头的符号和结尾的符号'^' + route + '(?:\\?([\\s\\S]*))?$',意思是说开头必须也route开头,后面可以跟参数,然后实例化这个字符串为正则对象并且返回

处理完正则之后,通过Backbone.history.route方法来监听该路由正则,如果在前进后退的时候路由匹配该正则就是执行回调

navigate (fragment, [options])

这个直接调用的Backbone.history.navigate(fragment, options);

  1. var r = new Backbone.Router;
  2. r.route("aa/:a/:a",function(){
  3. alert(Array.prototype.join.apply(arguments))
  4. })
  5. Backbone.history.start()
  6. r.navigate("aa/bb/cc",true)
  7. //传入bb cc 两个参数,可以打印出来

routes _bindRoutes

如果设置了routes,可以用_bindRoutes一次性绑定对routes里面的路由监听,_bindRoutes里面就是遍历routes,调用this,route进行绑定

  1. Backbone.Router = Backbone.Router.extend({
  2. alertA : function(){alert("a")}
  3. })
  4. var r = new Backbone.Router({
  5. routes : {
  6. "aa" : "alertA",
  7. "bb" : function(){alert("b")},
  8. }
  9. })
  10. Backbone.history.start()
  11. //先扩展alertA方法 然后通过routes批量绑定,如果value对应的是字符串,则会在实例化的r上找该方法,如果是函数就执行该函数
  12. r.navigate("aa",true);
  13. r.navigate("bb",true)

backbone.Router History源码笔记的更多相关文章

  1. Golang构建HTTP服务(一)--- net/http库源码笔记

    搭建一个简单的Go Web服务器 Go语言标准库 - net/http 在学习Go语言有一个很好的起点,Go语言官方文档很详细,今天我们学习的Go Web服务器的搭建就需要用到Go语言官方提供的标准库 ...

  2. Backbone事件模块源码分析

    事件模块Backbone.Events在Backbone中占有十分重要的位置,其他模块Model,Collection,View所有事件模块都依赖它.通过继承Events的方法来实现事件的管理,可以说 ...

  3. Zepto源码笔记(一)

    最近在研究Zepto的源码,这是第一篇分析,欢迎大家继续关注,第一次写源码笔记,希望大家多指点指点,第一篇文章由于首次分析原因不会有太多干货,希望后面的文章能成为各位大大心目中的干货. Zepto是一 ...

  4. redis源码笔记(一) —— 从redis的启动到command的分发

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载联系作者并保留声明头部与原文链接https://luzeshu.com/blog/redis1 本博客同步在http://www.cnblog ...

  5. AsyncTask源码笔记

    AsyncTask源码笔记 AsyncTask在注释中建议只用来做短时间的异步操作,也就是只有几秒的操作:如果是长时间的操作,建议还是使用java.util.concurrent包中的工具类,例如Ex ...

  6. Java Arrays 源码 笔记

    Arrays.java是Java中用来操作数组的类.使用这个工具类可以减少平常很多的工作量.了解其实现,可以避免一些错误的用法. 它提供的操作包括: 排序 sort 查找 binarySearch() ...

  7. Tomcat8源码笔记(八)明白Tomcat怎么部署webapps下项目

    以前没想过这么个问题:Tomcat怎么处理webapps下项目,并且我访问浏览器ip: port/项目名/请求路径,以SSM为例,Tomcat怎么就能将请求找到项目呢,项目还是个文件夹类型的? Tom ...

  8. Tomcat8源码笔记(七)组件启动Server Service Engine Host启动

    一.Tomcat启动的入口 Tomcat初始化简单流程前面博客介绍了一遍,组件除了StandardHost都有博客,欢迎大家指文中错误.Tomcat启动类是Bootstrap,而启动容器启动入口位于 ...

  9. Tomcat8源码笔记(六)连接器Connector分析

    根据 Tomcat8源码笔记(五)组件Container分析 前文分析,StandardService的初始化重心由 StandardEngine转移到了Connector的初始化,本篇记录下Conn ...

随机推荐

  1. Java中static的用法

    static静态,作为修饰符,最初是由c引入,一开始static表示退出一个块后依然存在的局部变量.随后,static表示不能被其他文件访问的全局变量和函数.到了C++和java,static表示属于 ...

  2. PostgreSQL停止动作观察

    实验过程如下: 启动一个客户端: [postgres@cnrd56 bin]$ ./psql psql () Type "help" for help. postgres=# be ...

  3. 编程异常——假设你报createSQLQuery is not valid without active transaction,...

    非常多时候我们使用hibernate的session时,都是让session在某一执行环境中保持其唯一. 比如在同一线程内用同一个session.在同一方法内用同一session,这样我们就能够用se ...

  4. linux C 执行多个文件

  5. android复制数据库到SD卡(网上搜集,未经验证)

    android中使用sqlite.复制assets下的数据库到SD卡.支持大于1M的文件 如果使用SD卡,需要在AndroidManifest.xml中设置权限 <uses-permission ...

  6. JavaScript OOP 思想

    JS的核心是对象 {}, new function(){}这种形式也是对象. http://www.nowamagic.net/librarys/veda/detail/241 整理一些网上的资料,供 ...

  7. input text框和 checkbox 连带被选中的情况

    <html> <head></head> <body> <ul> <li><input type="checkb ...

  8. 网络编程之PC版与Android手机版带断点续传的多线程下载

    一.多线程下载         多线程下载就是抢占服务器资源         原理:服务器CPU 分配给每条线程的时间片相同,服务器带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢占到更多的服 ...

  9. JS的setTimeout函数第一个参数问题

    setTimeout的第一个参数只能放一个无参的函数,更像放了一个函数指针在那里,如果要放带参数的话,就要拿个匿名函数包裹一下

  10. listen和accept函数

    listen函数是用来设置监听连接的句柄和队列 当listen函数执行完成以后,服务端就已经可以接受客户端来的新连接了,新连接完成以后listen会把客户端的ip,port和连接句柄放在监听队列里面, ...