Backbone源码解读(三)

注意:强烈建议一边阅读源码一边阅读本文。html

终于到了backbone源码解读的最后一篇,这一篇和前面几篇时间上有必定的间隔(由于要回学校有一堆乱七八糟的事...)。在这一篇里面会讲解Bakcbonesync & router & histrorysync比较简单,可是路由的部分就比较复杂了。我的以为是整个backbone源码里面最很差懂的一个部分,这个部分也使得backbone能够方便实现能够“返回”的单页面应用。我的以为这个部分其实并无很MVC有很密切的关系,可是它很是重要。读过源码就会发现,其实这一个部分与其余的模块(ModelCollection & View)相对独立。若是不但愿使用backbone但但愿能用到这个路由系统的话估计拆出来难度也不会很大。node

整体来讲路由模块由RouterHistrory组成,而Router事实上能够当作是对History的一个封装。用户直接操做的部分时Router,相关函数的处理(路由匹配时调用的函数)也是在Router中完成。History主要是处理一个更加棘手的问题,就是有关连接的问题。这里面有关于跨浏览器的解决方案是很是值得学习的。jquery

1. Router

Router是对History的封装,也是给用户定义路由的接口。通常来讲,用户在使用Router的时候会定义一个routers的对象,里面是想关路由与处理函数组成的key-valueRouter代码中主要作两件事,一件是对正则表达式的操做(能够看见里面不少使人痛苦的正则表达式),另外一件事就是事件相关的绑定了。Router的相关代码很少,写得比较精简。git

1.1 函数绑定与执行

Router里面定义了两种方法来定义路由事件,一个方法是用户定义routers,经过初始化调用_bindRoutes函数;一个方法是用自带的route函数。其实两个方法本质上都是调用了route函数_bindRoutes里面实际上也是经过循环来调用route函数。所以路由函数重点在于route函数。github

route函数里面,一开始是进行一些参数的整理。而后就是调用了History模块的route函数,把正则(匹配参数用的)和一个回调函数传了进去。在回调函数里面就是作 执行函数——触发router的事件——触发history的事件这几个步骤,在Historyroute函数里面,只是简单的插入地把keycallback组成对象插入handlers数组里面而已。ajax

1.2. 正则表达式转化与使用

Router函数里面最难懂也很是重要的部分是格式的转换。在Router里面有两个重要的函数_routeToRegExp函数和_extractParameters两个,这两个函数与正则密切相关。正则表达式

_extractParameters函数的做用是利用正则表达式,把传进来的url片断fragment分割成片断存进数组当中。这些片断是真实的已经匹配出来的参数,在route函数里面会把这些参数传给用户定义的函数里面,供用户使用。segmentfault

_routeToRegExp函数是一个简单,但要彻底理解很难的函数。这个函数的做用就是返回一个RegExp对象,经过这一个对象来匹配当前的连接,而后从中获得参数。进入这个函数以后会经过字符串的replace函数,匹配出路由的是哪几(或一)种状况,而且替代成能够捕获参数的正则字符串。好比说把路由定义里的/:page或者*fragment这样的字符串经过事先定义好的几个正则匹配到,而后换成带()的能够捕获的形式,而后在建立正则去捕获真正须要的常数。api

2. History

backbone中对于History模块的使用是经过用构造方式调用(new)返回一个可使用prototype方法的对象来实现的。Backbone.history = new History; 这个模块很是重要,并且在整个backbone里面能够说是最难彻底读懂的。下面我会从三个方面来说:一个方面是有关于路径格式处理的问题,在这方面也有不少和正则表达式相关的函数;另外一个方面是最关键的一个方面,就是History检测浏览器来使用不一样的路由控制方式;最后一个方面就是经过具体的函数来说解它是如何实现第二点各方面所说的控制的。数组

History从接口的角度来讲有start函数做为初始化的设置,还有经过Router模块封装的navigate方法。Router里面的不少处理须要调用到这个模块的方法。

History事实上也是对location/history必定程度上的封装。不少时候是经过location模块来读取匹配,经过history的一些方法来进行路由控制。

2.1 路径格式处理

2.1.1 root

用户能够设置root,做为根路径。这个根路径在模块中有一些判断和处理的地方。比方说肯定当前是否在根路径,或者在当前URL提取出相应的锚点等等都须要用到root这个内部变量。

2.1.2 getFragment

在这个函数里面咱们能够看到URL的格式分为了两种。一种是hash方式,一种是search方式(主要是兼容较老的浏览器)。在这里经过判断来进行浏览器能力检测。对于大部分现代浏览器来讲,事实上大都是使用hash方式获取锚点#后面的URL片断。

2.2 路由控制的三种方法(核心)

2.2.1 onhashchange方式

这种方式是经过监听'hashchange'事件,而后触发事件,用location.hash.replace方法来改变路由。

2.2.2 pushstate方式

这种方式是最为推荐的HTML5方式。使用historypushState方法修改history里的记录,而后也能够经过监听popstate来触发一些相关的事件。

2.2.3 iframe方式

这是一个很是巧妙可是从某种程度上很是“丑陋”的方法。丑陋是在它比较吃性能,一方面它有不少dom的操做来设置iframe,最重要的方面是它还用了定时器每隔一小段时间就检测,而后就触发函数,判断是否改变等等。插入一个空的iframe(通过属性设置)的做用在这里是存储hash的值和存储hash改变记录。在这里我遇到了一个问题:存储hash的值彻底能够经过一个全局变量来完成,为何要大费周折建立一个iframe呢?下面是我的的一些猜想:

Opening and closing the iframe tricks IE7 and earlier to push a history entry on hash-tag change.

这是源码中的一句注释。用iframe的理由多是为了经过开关iframe来存储记录。说实话具体是什么原理还不清楚,若是有人了解的话欢迎指教~

// 开关`iframe`
iWindow.document.open();
iWindow.document.close();

2.3 实现(start, navigate & 事件)

2.3.1 start

start主要作以下操做:

  • 进入start函数以后会把started设置为true防止重复出发。

  • 设置各类参数,用于后期判断使用哪种路由控制方式。

  • 若是有hashchange事件,但没有pushState方法,就用location.replace方法来改变路由。若是二者都有就调用navigate函数,里面能够经过pushState改变并记录路由。若是二者都没有就设置iframe并启动,经过设置iframehash参数来改变路由。

  • 绑定事件,用hashChange方法的绑定hashchange事件,用pushState方法的绑定popstate事件,用iframe的使用setInterval来监听。

2.3.2 navigate

进入函数以后首先是进行“组合”,“组合”出url。这个过程须要有rootfragment,后者须要调用getFragment函数,前者须要根据是path仍是hash来对root进行处理。若是是hash就不须要加/,若是是path就要加/。解码后判断当前的this.fragment和有没有发生变化,没有无论,有就更新。

根据浏览器使用不一样的方法。注意这里使用的判断的依据是在start函数里面就定义好的。

  • 若是有pushState或者replaceState就用;

  • 若是有hashchange就仅仅只调用_upadateHash,传入当前的location bom对象,里面用了location.replace,更新当前的href或者hash

  • 若是没有hashchange就须要把当前locationiframe.location对象分别传入_updateHash,而后更新当前href或者hash

    还有一个须要注意的是是否replace,这是一个传入参数,判断时候要影响history

2.3.3 事件

关于路由触发事件是经过两个函数来完成的,它们分别是checkUrlloadUrl, 前者会检测路由是否发生了改变,若是改变了就会触发navigate函数并调用loadUrl函数,然后者会经过路由片断来找到handlers相关的事件函数来触发。这就实现了用户在routes对象里面设置的事件了。

3. Sync

最后来个简单的Sync的讲解吧~有关ajax的部分在backbone中实际上是经过Backbone.ajax函数来代理jquery或者其余能够发起ajax的库的。而Sync函数事实上主要的工做就是部署ajax参数,最后调用这个Backbone.ajax发起请求。经过源码能够看到,其中为params设置了type, dataType, url, contentType, data, (processData)属性来做为发起ajax的参数。其中也为options设置了beforeSend, error方法做为ajax的回调(success函数写在其余模块中,详情能够看我以前的几篇文章)。

其中还须要主要的有两个参数emulateJSON & emulateHTTP。在文档中的介绍很是详细,我的以为在大部分时候都不会用到。

4. 最后的话

终于把最后的第三篇文章写出来了...花了很长时间...仍是以为若是要真正彻底读懂backbone源码要多读代码(惭愧,自认为还没彻底达到),多查资料,多读一些源码解析。有关于routerhistory我以为这篇文章仍是很棒的。这一个部分我的感受确实不是很好懂,可是能够学习到不少有关路由的处理的相关知识,实际上是很是有益的~

backbone被称为框架的框架。这个框架的思想比起使用更有意义,毕竟如今有更多功能强大的框架。新东西要学,可是经典也是不该该被落下的。

若是这篇文章有什么错误的地方请轻喷~互相学习!谢谢你们。

下面是所有的文章:
基于 Backbone + node 的我的简历生成器(我的学习总结)
Backbone源码解读(一)
Backbone源码解读(二)
Backbone源码解读(三)

相关文章
相关标签/搜索