backbone库的结构http://www.cnblogs.com/nuysoft/archive/2012/03/19/2404274.html

本文的例子来自http://blog.csdn.net/eagle_110119/article/details/8842032

Backbone.Router担任了一部分Controller(控制器)的工作,它一般运行在单页应用中,能将特定的URL或锚点规则绑定到一个指定的方法(后文中称Action)。

当我们开发一个单页应用时,常常会遇到这样两个问题:

  我们在同一个页面中通过用户的操作来隐藏、显示HTML块,为用户提供一个无刷新、完整流畅的体验,但用户可能并不知道他当前正处于同一个页面中,因此他希望通过浏览器的“前进”和“后退”按钮来返回和前进到上一步操作。当他真正这样操作时,会离开当前页面,这显然不是用户所期望的。

  另一个问题是用户在单页应用中操作,当他读到一篇好的文章,或看到一个中意的商品时,他可能会将URL收藏起来或分享给自己的好友。但当他下一次重新打开这个链接地址,看到的却是应用的初始化状态,而并不是当初那篇文章或那个商品。

以上来自http://blog.csdn.net/eagle_110119/article/details/8842032

1.1  Router

ok,下面我们先上例子再看:

  1. var AppRouter = Backbone.Router.extend({
  2. routes : {
  3. '' : 'main',
  4. 'topic' : 'renderList',
  5. 'topic/:id' : 'renderDetail',
  6. '*error' : 'renderError'
  7. },
  8. main : function() {
  9. console.log('应用入口方法');
  10. },
  11. renderList : function() {
  12. console.log('渲染列表方法');
  13. },
  14. renderDetail : function(id) {
  15. console.log('渲染详情方法, id为: ' + id);
  16. },
  17. renderError : function(error) {
  18. console.log('URL错误, 错误信息: ' + error);
  19. }
  20. });
  21.  
  22. var router = new AppRouter();
  23. Backbone.history.start();

这里我们自定义的route,main,renderList,renderDetail和renderError都会被绑定到AppRouter的原型上,这里与之前的model,collection和view是一样的。

下面代码:

  1. var router = new AppRouter();

我们来看一下构造器:

  1. var Router = Backbone.Router = function(options) {
  2. options || (options = {});
  3. if (options.routes) this.routes = options.routes;//可以在实例中定义路由规则,而且权值高,将不使用原型上的routers规则
  4. this._bindRoutes();//进入_bindRoutes方法
  5. this.initialize.apply(this, arguments);//执行initialize,使用时自己定义扩展
  6. };

这里initialize依旧需要我们使用的时候自行定义,ok,我们进入_bindRoutes方法。

  1. _bindRoutes: function() {
  2. if (!this.routes) return;//没有定义routes规则,将返回
  3. this.routes = _.result(this, 'routes');
  4. var route, routes = _.keys(this.routes);//获取属性名
  5. while ((route = routes.pop()) != null) {
  6. this.route(route, this.routes[route]);//依次执行route方法
  7. }
  8. }

关于_的result方法和key方法,之前我们都已经提及过,看一下原型上的route方法

这里方法比较麻烦,充斥着正则表达式,我们先将源码,分开阅读,先弄懂正则,再去理清逻辑。

  1. route: function(route, name, callback) {
  2. if (!_.isRegExp(route)) route = this._routeToRegExp(route);//`转成相应的正则表达式
  3. if (_.isFunction(name)) {
  4. callback = name;
  5. name = '';
  6. }
  7. if (!callback) callback = this[name];//将同名的方法相关联
  8. ..........

看一下_的isRegExp方法

  1. each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
  2. _['is' + name] = function(obj) {
  3. return toString.call(obj) == '[object ' + name + ']';
  4. };
  5. });

不多说,我们看一下_routeToRegExp方法

  1. _routeToRegExp: function(route) {
  2. route = route.replace(escapeRegExp, '\\$&')// /[\-{}\[\]+?.,\\\^$|#\s]/g,给所有匹配的字符前面加转义符'\'
  3. .replace(optionalParam, '(?:$1)?')// /\((.*?)\)/g
  4. .replace(namedParam, function(match, optional) {
  5. return optional ? match : '([^\/]+)'; // /(\(\?)?:\w+/g
  6. })
  7. .replace(splatParam, '(.*?)');
  8. return new RegExp('^' + route + '$');
  9. }

分析之前,我们先看一下这4个正则

  1. var optionalParam = /\((.*?)\)/g;
  2. var namedParam = /(\(\?)?:\w+/g;
  3. var splatParam = /\*\w+/g;
  4. var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;

这里我们逐一讲下,/g标识全局匹配。先看optionalParam

分解一下:'\('匹配'(','()'表示子表达式,其中的'.*?',表示匹配任何字符,但是要忽略优先,像*这样的量词,正则默认是匹配优先的,可以理解为尽可能的去匹配更多的字符,如果在其后加了?,则表示在完成的要求上,尽可能少匹配。然后'\)'

匹配一个')',源码中有用replace方法,注意其中的\\$&,这个则表示,将正则匹配的结果前面添加'\',这里$&就是标识正则匹配的结果。注意这里的.*?因为最后有个)需要匹配,所以文本中的()的所有字符都必须匹配,这样你写成(.*)也能达到要求,其实就是,虽然告诉正则引擎要尽可能的少匹配,但是不能匹配最后的)仍然需要继续匹配。

namedParam:分解成两块:(\(\?)? 和:\w+,前者是一个子表达式,它匹配'(?',匹配数是没有或者1个,后者是匹配':',然后是至少一个字符(字母或数字或下划线或汉字)。

splatParam: 这个简单,匹配'*',然后是至少一个字符(字母或数字或下划线或汉字)

escapeRegExp:这个很长,其实很简单,先看[..........],表示分组,即表示只需要匹配分组中的任意一个就可以了,再看分组的内容有:'[','-','{}',']','+','?','.','\','^','$','|','#',空格(空字符串)

这个方法只是将我们传入的字符串,进行装饰,变成需要的正则表达式。

继续看route方法的后半部分

  1. .....
  2. var router = this;
  3. Backbone.history.route(route, function(fragment) {//执行route方法
  4. var args = router._extractParameters(route, fragment);
  5. callback && callback.apply(router, args);
  6. router.trigger.apply(router, ['route:' + name].concat(args));
  7. router.trigger('route', name, args);
  8. Backbone.history.trigger('route', router, name, args);
  9. });
  10. return this;
  11. }

调用了Backbone库的history原型上的route方法,传入了正则规则,和回调函数。我们看一下Backbone.history.route这个方法

  1. route: function(route, callback) {
  2. this.handlers.unshift({route: route, callback: callback});//从头插入。将相关正则和触发函数关联后,存入对象属性数组
  3. }

将相关正则和触发函数关联后,存入对象属性数组中,以上完成了new Router构造器的任务,例子中还有一个

  1. Backbone.history.start();

1.2  History

例子中使用了与history相关的模块,那我们先不看start这个方法,由于页面中我们并没有实例化相关的history,很有可能history是在backbone库中自行实例化的,那我们先看一下结构:

  1. var History = Backbone.History = function() {} //history构造器
  2. var routeStripper = /^[#\/]|\s+$/g; //相关正则
  3. ...
  4. History.started = false;
  5. _.extend(History.prototype, Events, {}) //history的原型
  6. Backbone.history = new History;//实例化History

老规矩,先看构造器

  1. var History = Backbone.History = function() { //history构造器
  2. this.handlers = [];
  3. _.bindAll(this, 'checkUrl');//将this中的属性绑定checkUrl函数
  4. // Ensure that `History` can be used outside of the browser.
  5. if (typeof window !== 'undefined') {
  6. this.location = window.location;//原生location信息
  7. this.history = window.history;//存在一条历史记录
  8. }
  9. }

history实例对象Backbone.History拥有了一些bom原生的参数信息。

在看start方法之前,我们再过一下history中存在的几条正则

  1. var routeStripper = /^[#\/]|\s+$/g;
  2. var rootStripper = /^\/+|\/+$/g;
  3. var isExplorer = /msie [\w.]+/;
  4. var trailingSlash = /\/$/;

我们逐条来看一下:

routeStripper:分解一下:首先看到|符号,它的前面是[#\/],表示分组,意思是文本开头可以是#或者/,如何都不是那可以匹配|后面的\s+,表示开头是空白字符。

rootStripper: 与上一条看上去相似,实际上不同,分解一下,首先看到|符号,它的前面是\/+表示文本开头可以至少有/,如果没有/则匹配|的后者\/+,感觉一样啊。。。

isExplorer:这个比较简单,匹配msie,后面至少一个字符(字母或数字或下划线或汉字)

trailingSlash:这个匹配文本结尾最后一个字符是/

下面我们来看一下start方法:

  1. start: function(options) {
  2. if (History.started) throw new Error("Backbone.history has already been started");//默认history.start属性是false,没有开启,如果没有调用就开启了肯定是要抛错的
  3. History.started = true;//调用start之后,将History.started设置为true。
  4.  
  5. // Figure out the initial configuration. Do we need an iframe?
  6. // Is pushState desired ... is it available?
  7. this.options = _.extend({}, {root: '/'}, this.options, options);//将参数,都传入对象的options属性中。该例子中只有{root:'/'}
  8. this.root = this.options.root;//默认是'/'一般系统默认是/,但只要你自定义,都可以被覆盖
  9. this._wantsHashChange = this.options.hashChange !== false;//这里默认hashChange为undefined,默认_wantsHashChange是true
  10. this._wantsPushState = !!this.options.pushState;//!!转成boolean型,默认this.options.pushState为undefined,所有这个例子中wantsPushState为false
  11. this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState);//因为pushState为undefined,所以hasPushState为false
  12. var fragment = this.getFragment();//这里没有传值,默认是undefined
  13. var docMode = document.documentMode;//获取IE版本(使用于IE8+)
  14. //navigator.userAgent取浏览器版本详细信息,以下考虑旧版本的IE(IE7-)
  15. var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));//低版本IE没有documentMode属性
  16.  
  17. // Normalize root to always include a leading and trailing slash.
  18. this.root = ('/' + this.root + '/').replace(rootStripper, '/');
  19. //兼容低版本IE
  20. if (oldIE && this._wantsHashChange) {
  21. this.iframe = Backbone.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
  22. this.navigate(fragment);
  23. }
  24.  
  25. // Depending on whether we're using pushState or hashes, and whether
  26. // 'onhashchange' is supported, determine how we check the URL state.
  27. if (this._hasPushState) {
  28. Backbone.$(window).on('popstate', this.checkUrl);
  29. } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {//非老版本IE
  30. Backbone.$(window).on('hashchange', this.checkUrl);//为window绑定hashchange监听事件,事件函数是checkUrl,主要是控制url的变化,
  31. } else if (this._wantsHashChange) {
  32. this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
  33. }
  34.  
  35. // Determine if we need to change the base url, for a pushState link
  36. // opened by a non-pushState browser.
  37. this.fragment = fragment;
  38. var loc = this.location;//获取当前location的信息
  39. var atRoot = loc.pathname.replace(/[^\/]$/, '$&/') === this.root;//处理路径名
  40.  
  41. // Transition from hashChange to pushState or vice versa if both are
  42. // requested.
  43. if (this._wantsHashChange && this._wantsPushState) {
  44.  
  45. // If we've started off with a route from a `pushState`-enabled
  46. // browser, but we're currently in a browser that doesn't support it...
  47. if (!this._hasPushState && !atRoot) {
  48. this.fragment = this.getFragment(null, true);
  49. this.location.replace(this.root + this.location.search + '#' + this.fragment);
  50. // Return immediately as browser will do redirect to new url
  51. return true;
  52.  
  53. // Or if we've started out with a hash-based route, but we're currently
  54. // in a browser where it could be `pushState`-based instead...
  55. } else if (this._hasPushState && atRoot && loc.hash) {
  56. this.fragment = this.getHash().replace(routeStripper, '');
  57. this.history.replaceState({}, document.title, this.root + this.fragment + loc.search);
  58. }
  59. }
  60. if (!this.options.silent) return this.loadUrl();//默认silent没有
  61. }

我使用的浏览器是ff,暂时我们认为源码的运行流程按非IE来看,(之后我们会看一下代码处理IE和低版本IE的部分)

先看一下

  1. var fragment = this.getFragment();

看一下getFragment方法:

  1. getFragment: function(fragment, forcePushState) {
  2. if (fragment == null) {
  3. if (this._hasPushState || !this._wantsHashChange || forcePushState) {
  4. fragment = this.location.pathname;
  5. var root = this.root.replace(trailingSlash, '');
  6. if (!fragment.indexOf(root)) fragment = fragment.slice(root.length);
  7. } else {
  8. fragment = this.getHash();//正则匹配去取锚点
  9. }
  10. }
  11. return fragment.replace(routeStripper, '');
  12. }

再看一下getHash()方法:

  1. getHash: function(window) {
  2. var match = (window || this).location.href.match(/#(.*)$/);//取子表达式所匹配的内容,实际上去取锚点,match结果第一个是匹配整个正则,第二个是$1
  3. return match ? match[1] : '';//如果子表达式匹配了,则返回,没有则返回空,实际上返回#之后的信息
  4. }

因为,我们的地址是file:///E:/backbone-learn/demo6.html是没有锚点的,所以,这个fragment返回应该是空字符串。

看到这部分代码:

  1. if (this._hasPushState) {
  2. Backbone.$(window).on('popstate', this.checkUrl);
  3. } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {//非老版本IE
  4. Backbone.$(window).on('hashchange', this.checkUrl);//为window绑定hashchange监听事件,事件函数是checkUrl,主要是控制url的变化,
  5. } else if (this._wantsHashChange) {
  6. this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
  7. }

这里只要是绑定了hashchange的监听事件,触发函数是checkUrl。看一下吧

  1. checkUrl: function(e) {
  2. var current = this.getFragment();//获取当前的锚点
  3. if (current === this.fragment && this.iframe) {//兼容低版本IE
  4. current = this.getFragment(this.getHash(this.iframe));
  5. }
  6. if (current === this.fragment) return false;//没有发生改变则返回
  7. if (this.iframe) this.navigate(current);
  8. this.loadUrl();
  9. }

这里getFragment实际上是一个获取当前锚点的方法。如果锚点发生改变了,事件会触发,获取最新的锚点,更换上去。哪里实现更换了?看一下loadUrl:

  1. loadUrl: function(fragmentOverride) {
  2. var fragment = this.fragment = this.getFragment(fragmentOverride);
  3. return _.any(this.handlers, function(handler) {
  4. if (handler.route.test(fragment)) {
  5. handler.callback(fragment);
  6. return true;
  7. }
  8. });
  9. }

在一次获取当前锚点,return了一个_.any的方法,看一下:

  1. var any = _.some = _.any = function(obj, iterator, context) {
  2. iterator || (iterator = _.identity);
  3. var result = false;
  4. if (obj == null) return result;
  5. if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);//支持原生some
  6. each(obj, function(value, index, list) { //自定义方法实现
  7. if (result || (result = iterator.call(context, value, index, list))) return breaker;
  8. });
  9. return !!result;
  10. }

对于some方法,我开始也不了解,于是乎百度了一下,给大家一个例子吧。(some的例子)   http://msdn.microsoft.com/zh-SG/library/ie/ff679978.aspx

这里浏览器支持some方法,理解一下一下代码:

  1. return _.any(this.handlers, function(handler) {
  2. if (handler.route.test(fragment)) {
  3. handler.callback(fragment);
  4. return true;
  5. }
  6. });

这里的handler.callback是什么,这个回调应该是:

  1. function(fragment) {//执行route方法,所有的正则绑定这个回调函数
  2. var args = router._extractParameters(route, fragment);
  3. callback && callback.apply(router, args);//这里的回调是我们自定义的触发函数
  4. router.trigger.apply(router, ['route:' + name].concat(args));//同时可以触发router+name的事件,该事件需要我们自行创建
  5. router.trigger('route', name, args);//同时触发route事件
  6. Backbone.history.trigger('route', router, name, args);//router与history相关联,触发history的route事件,以上一个触发事件需要我们在页面定义。
  7. }

如果你乱了,请看一下router的构造器,不记得的,一路做一点记录。其中一个代码if(handler.route.test(fragment))可以这样理解,我们在初始化router构造器的时候,传入了一系列的hash规则,现在规则定义,如果我们传入了一个锚点,或是监听到了一个锚点的改变,取到这个这个锚点(最新的锚点)去这个我们先前定义好的hash规则中去匹配,如果匹配成功,则执行相应的我们定义的hash回调函数,此时这里的回调函数,才是我们之前自己定义的回调函数。如果我们修改的url,这里的hash规则没有一个能匹配,则返回fslse。这边回调有点多,大家耐心理解下,如果我哪里理解错了,也麻烦提出来修正。

ok,如果匹配成功了,第一会执行我们自定义的响应方法(回调函数),系统内部会触发三个监听事件,这三个监听事件需要你事先写在页面中

1:route:name类型的监听事件

2:route类型的监听事件

3:之前两个是router上的监听事件,这个是history的监听事件。是也是route类型的

至此,我们完成了FF下的start流程。

1.3  路由规则设定

从上面的例子中就能看出,它可以将不同的URL锚点导航到对应的Action方法中。

以上的源码分析,我们也应该清楚Backbone的路由导航是由Backbone.Router和backbone.History两个类共同完成的。

这里例子中给出了概括的描述:

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

History类用于监听URL的变化,和触发Action方法。

1.4   Hash规则

例子中给一些描述:

我们再根据例子看一下:

  1. http://localhost/index.html // 输出:应用入口方法
  2. http://localhost/index.html#topic // 输出:渲染列表方法
  3. http://localhost/index.html#topic/1023 // 输出:渲染详情方法, id为:1023
  4. http://localhost/index.html#about // 输出:URL错误, 错误信息: about

下面是我们定义的hash规则

  1. routes : {
  2. '' : 'main',
  3. 'topic' : 'renderList',
  4. 'topic/:id' : 'renderDetail',
  5. '*error' : 'renderError'
  6. }

注重看一下topic/:id和*error这两个规则,经过_routeToRegExp方法会生成什么规则

topic/:id

*error

看一下生成的这两个正则,topic/([^/]+),这个字符串在生成正则时,会转成

topic\/([^/]+),这个正则可以匹配topic/后面是一个非/的任意字符(至少要有一个,空格也是可以的)。另外,topic后面的内容用()包裹,可以获取这个子表达式的值,这也是为什么我们可以取到topic/后面的信息了

第二条(.*?)

()是一个子表达式,可以用$1或$&获取,其中.*?,标识忽略优先,或者叫非贪婪。尽可能少的去匹配达到完成任务,但如果一次不匹配,还是要继续匹配,宗旨是尽可能少。

我们如何获取子表达式的匹配内容呢?在_extractParameters方法里

  1. var params = route.exec(fragment).slice(1);//匹配我们生成的正则,这里获取子表达式的信息

1.5   pushState规则

backbone.history还支持pushState方法的URL,pushState是HTML5提供的一种新特性,它能操作当前浏览器的URL(而不是仅仅改变锚点),同时不会导致页面刷新,从而使单页面,从而使单页面应用使用起来更像一套完整的流程。

HTML5的部分,暂时不做讨论。

1.6  路由相关方法

route方法,navigate方法,stop方法

1.6.1  route

先看route方法,上例子:

  1. router.route('topic/:pageno/:pagesize', 'page', function(pageno, pagesize){
  2. // todo
  3. });

route方法,之前分析过了,上述的例子告诉我们,除了将hash规则和触发函数写在原型上,也可以直接写在实例上。

1.6.2  navigate

先上例子:

  1. router.navigate('topic/1000', {
  2. trigger: true
  3. });

看一下navigate方法:

  1. navigate: function(fragment, options) {
  2. Backbone.history.navigate(fragment, options);
  3. return this;
  4. }

调用的是History的navigate方法

  1. navigate: function(fragment, options) {
  2. if (!History.started) return false;//没有启动,则返回
  3. if (!options || options === true) options = {trigger: !!options};
  4. fragment = this.getFragment(fragment || '');
  5. if (this.fragment === fragment) return;//没有发生变化,则返回,返回刷新无效
  6. this.fragment = fragment;//将定义的hash传入fragment属性中
  7.  
  8. var url = this.root + fragment;//拼接url
  9.  
  10. // Don't include a trailing slash on the root.
  11. if (fragment === '' && url !== '/') url = url.slice(0, -1);
  12. // If pushState is available, we use it to set the fragment as a real URL.
  13. if (this._hasPushState) {
  14. this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
  15.  
  16. // If hash changes haven't been explicitly disabled, update the hash
  17. // fragment to store history.
  18. } else if (this._wantsHashChange) {
  19. this._updateHash(this.location, fragment, options.replace);//更新hash
  20. if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
  21. // Opening and closing the iframe tricks IE7 and earlier to push a
  22. // history entry on hash-tag change. When replace is true, we don't
  23. // want this.
  24. if(!options.replace) this.iframe.document.open().close();
  25. this._updateHash(this.iframe.location, fragment, options.replace);
  26. }
  27.  
  28. // If you've told us that you explicitly don't want fallback hashchange-
  29. // based history, then `navigate` becomes a page refresh.
  30. } else {
  31. return this.location.assign(url);//跳转到该url
  32. }
  33. if (options.trigger) return this.loadUrl(fragment);//设置trigger后,重新执行正则匹配,触发监听事件等,如果不设置,将不会触发相应关联方法和监听事件
  34. }

这个方法大概的作用是这样的,先获取传入的hash,判断是否变化了,如果变化了,先将其更新hash,更新方法如下:

  1. _updateHash: function(location, fragment, replace) {
  2. if (replace) {
  3. var href = location.href.replace(/(javascript:|#).*$/, '');
  4. location.replace(href + '#' + fragment);
  5. } else {
  6. // Some browsers require that `hash` contains a leading #.
  7. location.hash = '#' + fragment;//更新hash
  8. }
  9. }

接下来,你可以设置trigger属性为true,也可以不设置,这就意味着,你是否需要执行loadUrl方法,这个方法帮你执行生成的正则匹配,执行关联的函数,监听的事件等。换言之,如果你不设置的话,将不会触发该一系列事件和函数方法。

1.6.3   stop

还记得我们是通过Backbone.history.start()方法来启动路由监听的,你也可以随时调用Backbone.history.stop()方法来停止监听,看例子:

  1. Backbone.history.stop();

看一下history原型上的stop方法:

  1. stop: function() {
  2. Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
  3. clearInterval(this._checkUrlInterval);
  4. History.started = false;
  5. }

清除事件的清除,清除定时器的清除定时器,最后将History.started置为false,这里为什么会有定时器呢?之前我们留了一个低版本IE的问题,下面我们来看一下

1.7  低版本IE的部分问题

回到history的start方法中:

  1. if (this._hasPushState) {
  2. Backbone.$(window).on('popstate', this.checkUrl);//HTML5
    } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {//非老版本IE
  3. Backbone.$(window).on('hashchange', this.checkUrl);//为window绑定hashchange监听事件,事件函数是checkUrl,主要是控制url的变化,
  4. } else if (this._wantsHashChange) {//低版本
    this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
  5. }

可以看到,对于不支持onhashchange的浏览器,history会使用定时器去监听它。

注意这些属性名,我们就大致了解了

1._hasPushState,拥有pushState属性

2._wantsHashChange,没有onhashchange事件

以下部分来自http://www.cnblogs.com/rubylouvre/archive/2012/10/24/2730599.html

最后提一提hash值的提取,这里存在两个兼容性问题:

IE6直接用location.hash取hash,可能会取少一部分内容:

比如 http://www.cnblogs.com/rubylouvre#stream/xxxxx?lang=zh_c

ie6 => location.hash = #stream/xxxxx

其他浏览器 => location.hash = #stream/xxxxx?lang=zh_c

firefox 会自作多情对hash进行decodeURIComponent

比如 http://www.cnblogs.com/rubylouvre/#!/home/q={%22thedate%22:%2220121010~20121010%22}

firefox 15 => #!/home/q={"thedate":"20121010~20121010"}

其他浏览器 => #!/home/q={%22thedate%22:%2220121010~20121010%22}

再比如如下代码:

history中的navigate方法

  1. if (this._hasPushState) {
  2. this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
  3.  
  4. // If hash changes haven't been explicitly disabled, update the hash
  5. // fragment to store history.
  6. } else if (this._wantsHashChange) {
  7. this._updateHash(this.location, fragment, options.replace);//更新hash
  8. if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
  9. // Opening and closing the iframe tricks IE7 and earlier to push a
  10. // history entry on hash-tag change. When replace is true, we don't
  11. // want this.
  12. if(!options.replace) this.iframe.document.open().close();
  13. this._updateHash(this.iframe.location, fragment, options.replace);
  14. }
  15.  
  16. // If you've told us that you explicitly don't want fallback hashchange-
  17. // based history, then `navigate` becomes a page refresh.
  18. } else {
  19. return this.location.assign(url);//跳转到该页
  20. }

关于pushState和replaceState,可以参考http://zhanchaojiang.iteye.com/blog/1462994

默认使用location.assgin完成跳转。不足之处,也欢迎园友们补充,谢谢。

很多时候,读码也是一种旅行,只要一杯茶,一段源码,一个浏览器,剩下的唯有坚持。

身体需要行走,我的精神依旧在路上....

内容不多,时间刚好,以上是我的一点读码体会,如有错误,请指出,大家共通学习。

backbone库学习-Router的更多相关文章

  1. backbone库学习-Collection

    backbone库的结构: http://www.cnblogs.com/nuysoft/archive/2012/03/19/2404274.html 本文所有例子来自于http://blog.cs ...

  2. backbone库学习-model

    backbone库的结构: http://www.cnblogs.com/nuysoft/archive/2012/03/19/2404274.html 本文所有例子来自于http://blog.cs ...

  3. backbone库学习-Events

    backbone库的框架 http://www.cnblogs.com/nuysoft/archive/2012/03/19/2404274.html 我们先从backbone的Events模块开始 ...

  4. backbone库学习-View

    Backbone中的视图提供了一组处理DOM事件.和渲染模型(或集合)数据方法(在使用视图之前,你必须先导入jQuery或Zepto) 视图类提供的方法非常简单,我们一般在backbone.View的 ...

  5. 【转】Backbone.js学习笔记(二)细说MVC

    文章转自: http://segmentfault.com/a/1190000002666658 对于初学backbone.js的同学可以先参考我这篇文章:Backbone.js学习笔记(一) Bac ...

  6. python 操作exls学习之路1-openpyxl库学习

    这篇要讲到的就是如何利用Python与openpyxl结合来处理xlsx表格数据.Python处理表格的库有很多,这里的openpyxl就是其中之一,但是它是处理excel2007/2010的格式,也 ...

  7. dlib库学习之一

    dlib库学习之一 1.介绍 跨平台 C++ 通用库 Dlib 发布 ,带来了一些新特性,包括概率 CKY 解析器,使用批量同步并行计算模型来创建应用的工具,新增两个聚合算法:中国低语 (Chines ...

  8. python_库学习_01

    一.python的库学习之 财经数据接口包 1.安装ThShare 直接pip install tushare 可能会出现缺少依赖库的情况,依次安装,大概有lxml,pandas,bs4,reques ...

  9. numpy, matplotlib库学习笔记

    Numpy库学习笔记: 1.array()   创建数组或者转化数组 例如,把列表转化为数组 >>>Np.array([1,2,3,4,5]) Array([1,2,3,4,5]) ...

随机推荐

  1. 精益VS六西格玛

    名称 精益方法 Six Sigma管理 假定 1)消除浪费可以改善绩效 2)大量的小改进更有利于组织成长 1)问题总是存在的: 2)测量是重要的: 3)随着变异减少,系统产出得到改进 文化基础 东方以 ...

  2. NSDate

    NSDate : NSDate *date = [NSDate date];获取当前日期 NSDate 可以进行比较,通过earlierDate:方法获取二个日期中最早的. NSDate 通过late ...

  3. testMarkDown

    title: git常用命令 date: 2015-10-27 10:28:25 categories: git tags: git 添加 SSH 公钥 Windows 平台中,可以使用 Git Ba ...

  4. Spring对Quartz的封装实现简单需注意事项

    前段时间在项目中一直使用正常的Quartz突然出现了任务漏跑的情况,由于我以前看过Quartz的内部实现,凭借记忆我觉得是由于Quartz的线程池的使用出现问题导致了故障的发生.为了搞清问题的真相,我 ...

  5. 微信公共平台开发-(.net实现)3--发送文本消息

    最近,项目这边比较忙,没来得及续写,哎,先吐吐槽吧,在这个周六还得来上班,以后每个周六多要上,一天的滋味真有点受不鸟呀.还不习惯ing... 嗯,别的不说了现在开始接着上次http://www.cnb ...

  6. RCP: JDT 根据org.eclipse.jdt.core.IJavaElement对象获取org.eclipse.jdt.core.dom.ASTNode对象

    JDT中有两套Java文件模型映射. 其核心类\接口分别为: org.eclipse.jdt.core.IJavaElement和org.eclipse.jdt.core.dom.ASTNode IJ ...

  7. STC12C5A60S2笔记1(管脚定义)

    STC12C5A60S2管脚定义 管脚1:标准IO口P1.0.ADC0 模数转换通道0.CLKOUT2 波特率发生器的时钟输出 管脚2:标准IO口P1.1.ADC1 模数转换通道1 管脚3:标准IO口 ...

  8. chrome浏览器渲染白屏问题剖析

    刚截图十几次,终于捕捉到了这个白屏现象,hiahia~~ 大家可以很清晰地看到下边还木有渲染完毕的透明层,这是一个十分普遍的问题,经常遇到.我的浏览器版本是 到目前为止应该是最新版(release版本 ...

  9. Entity Framework 5.0系列之数据操作

    Entity Framework将概念模型中定义的实体和关系映射到数据源,利用实体框架可以将数据源返回的数据具体化为对象:跟踪对象所做的更改:并发处理:将对象更改传播到数据源等.今天我们就一起讨论如何 ...

  10. SiteMesh3整合SpringMVC+FreeMarker

    SiteMesh3配置 添加maven依赖 添加filter 配置servlet 添加sitemesh配置文件 decorator示例 SpringMVC.FreeMarker配置(404问题处理) ...