Backbone设计思路和关键源码分析

一. Backbone的江湖地位:

backbone作为一个老牌js框架为大规模前端开发提供了新的开发思路:前端MVC模式,这个模式也是前端开发演变过程中的一个重要里程碑,也为MVVM和Redux等开发思路奠定了夯实的基础,后来的react,vue无不是在backbone的影响下开创出来的经典模式。为什么这么说呢?我们先来回顾下Web前端开发的大概演变流程,本过程纯粹个人理解,抛砖引玉,共同探讨,如有偏差请看官指出错误:

1. 无前端:最早的网页就是HTML,还只是静态页面,当时的脚本含量极少甚至没有js,js只是作为一个辅助工具而已。

2. 萌芽:由于用户体验问题(比如文本校验等等),开发人员把服务端脚本(比如python,perl)等语言搬到到浏览器端,也就是JavaScript,让浏览器可以分担一部分任务,而不需要请求后台服务器,这在当时网络只有几十KB的环境下,用户体验效果提升非常明显。

3. 混沌:这个时候各个网站为了提升用户体验,丰富网站能力,开始了各种各样的尝试,ASP,PHP,JSP等等各种模式迅速成长起来,服务器脚本和JS脚本共同协作把数据展现在页面中,处理用户交互请求,页面效果和能力得到大大提升,互联网在这个时候开始突飞猛进的发展起来。然而此时的架构只是单单解决了功能需求,却没有考虑系统的维护性,扩展性和性能,安全等各个方面,前后端尚未分离,还处于混沌阶段。那个时候还没有Web前端开发岗位,只有PHP,ASP或者JSP开发人员。

4. 后端MVC:由于项目的日渐扩大,系统的复杂度也日渐加深,Web程序愈发臃肿,维护性越来越成问题,于是出现了后端MVC模式,比如Structs、Spring MVC,ASP.NET等等,这种模式把服务端逻辑管理起来,以Model,View,Controller的模式来开发,将数据,视图和操作逻辑分离开来,大大提高了代码的清晰度和可维护性,让之前的混沌局面有了很好的改观。然而此时此刻前后端仍没有分离,HTML,JS,后台脚本语言(PHP,JSP,ASP,ASP.NET)等等仍然混杂在一起,当需求频繁变化时,视图和逻辑修改仍然不能完全分离。

5. Ajax:后来就是出现了大名鼎鼎的ajax以及各种对应框架,让前后端得到了分离,前端处理UI展现,用户交互,后台处理数据查询,更新,后台逻辑计算等等。从此Web前端开发诞生,部分开发人员开始专注于Web前端领域,开始了历史上的分久必合合久必分的"分"。此时很多逻辑被划分到浏览器端,前端js的代码规模开始日渐复杂起来,这时候出现很多经典框架比如大名鼎鼎的jQuery,其高速处理DOM,屏蔽浏览器兼容性,事件封装,ajax封装等功能确实非常好用。

6. 模块化:随着js代码越来越复杂,规模越来越大,扩展性和维护性又出现在开发人员面前,同时由于js语言的特性,没有模块或者说包的概念,导致js语言容易引起变量冲突,脚本依赖冲突等等,代码复用率低等等问题,为了解决这些问题,Web前端进入到了模块化开发时代,这个时代英雄辈出,包括YUI,AMD,CMD等等(requireJs,seaJs),在这个阶段js代码被模块化起来,代码的维护性,复用度得到了极大的提升。

7. 前端MVC:模块化开发虽然解决了纯逻辑模块的代码维护性问题,但是Web开发毕竟是处理用户界面操作,不可避免的要处理各种UI视图问题,而js必须要和HTML,数据进行紧密的配合,而这些代码依然是混杂状态,最常见的场景就是js去操作各种DOM,HTML模版等等,代码的耦合度很高,维护性很低。自此BackboneJS、EmberJS、KnockoutJS、AngularJS的出现,让这些问题有了改观,通过将后端MVC模式引入到前端,将前端逻辑也划分为Model,View,Controller等模式,比如今天的主角Backbone,他定义了Model,Collection,View等模式,把代码的逻辑规范起来,有了一定的套路,极大的提高了开发效率,降低了代码耦合性,这里特别说明一下,由于Web开发的特殊性,Contoller是无法完全从HTML分离开来的,所以在Backbone中,没有独立的Contller模块,而是在Model和View,还有Router中体现出来,Model,View和Router都可以发起Controller逻辑操作。在这个阶段,前端大规模开发终于有了自己的套路。

8. MVVM:MVVM是MVC在View层的一个优化,由于MVC开发过程中,View层会出现很多DOM的查询,操作等等,非常繁琐复杂,DOM性能也出现瓶颈,同时由于HTML标签的局限性,UI层面的开发比较繁琐,为此Vue和React提出了MVVM的模式,在这个模式下开发人员彻底抛弃DOM,以数据和View的绑定式开发,大大减少了Web开发中DOM操作的代码量,同时提出了虚拟DOM的模式,一定程度上也提升了DOM操作的性能,其次,提出了全组件式开发,丰富了HTML标签的能力,通过创建组件的方式,创建了新的HTML标签,以组件的方式来开发Web页面,从而降低了页面代码的耦合度,提升了Web开发的效率。

9. Redux/Vuex:在大规模Web开发过程中,仅有MVVM是不够的,MVVM只是对DOM进行了抽象,但是另外两个问题还需要解决:代码组织结构和组件通信。MVC模式也是为了解决前端复杂度问题,但是仍然不是最佳方案,试想以下的情景:

 VS  

通过Controller来驱动Model和View的逻辑,当Model和View有成千上百的规模的时候,你会发现,这里的逻辑觥筹交错,有时候并不能很明确的知道到底是哪里改变了当前的视图,同时当一个数据发生变化以后,又会有多少个View会发生变化,完全不可预测,甚至失控。为此开发人员借用了Flux的开发思路,提出了Redux的开发模式,他提出三大原则,在这个模式下,数据单向流动对UI产生影响,让开发人员可以清晰的看到数据的流向,开发人员只对数据进行操作,View由数据发生变化而刷新,从而使整个逻辑的流向非常清晰可追溯,可控。

10. Node全栈时代:"话说天下大势,分久必合,合久必分",随着前后端分离多年之后,Node的出现让全栈开发又重新回到了前端开发的视野,Web开发人员不甘于仅限在浏览器端折腾,V8的出现让开发人员可以通过js语言在服务器端开发,至此,是不是说Node又要一匡天下了呢?

 二. Backbone介绍

好了废话不多说,到这里你应该知道BackboneJs在历史中的来历和地位了吧?虽然Backbone有着自己的优缺点,而且现在已经不再是Backbone风光的时代了,但是了解Backbone的设计思想和源码结构,还是对我们日常开发还是非常有帮助的。我们首先看几个问题

1. backbone是什么?backbone带来了什么?为什么要使用backbone?

(1)首先backbone是一个前端MVC框架,为大型Web程序提高了一个骨架的作用,让开发人员更好的组织和设计代码,他需要跟understore和jQuery一起配合。

(2)backbone带来了MVC模式,分离了UI和数据,带来了事件通信机制,带来了单页面开发的便捷方式。没有MVC之前的粗犷模式有哪些问题:

a) 数据保存在内部变量中和业务模型无关,杂乱零散不可控

b) 代码复用率低,模块化后有些改观,但无法从业务层面复用

c) 代码结构耦合在一起,不清晰,维护成本高

d) 需求变化时,由于维护成本居高不下,更新需求的开发效率底下。

而MVC模式从一定程度上改观了上述问题。

(3)当你的项目中很多页面属于单页面模式,并且有很多DOM操作,数据的查询,修改等等,你可以使用backbone更好的组织代码,提高开发效率,降低维护成本。

2. 他有什么优点?

(1)View可以划分UI,UI的复用性更高,增加UI的内聚度,降低耦合

(2)View和Model事件机制进行通信,组件更加独立

(3)MVC架构让代码结构清晰可维护

(4)CRUD 比ajax请求更加便捷

3. 他有什么缺点?

(1)Model设计比较简单,无法满足复杂数据关系,如多对多的数据关系等等

(2)写代码需要小心,避免内存泄漏问题

4. 设计思路是什么?

Backbone的设计思路,就是把UI分为View来定义,数据分为Model来定义,多个Model可以保存在Collection,在Model,Colletion和Router中实现CURD操作,通过事件驱动来更新View,主要有3种事件驱动

(1) 浏览器DOM事件:绑定在DOM中的浏览器事件

(2) 模型事件:Model和Collection都有save,change等事件

(3) 路由事件:路由变化事件

最终的效果就是用MVC模式来管理组织代码。

我们可以看到上面的流向和React和Vue相比其实还是有些杂乱的,但在当时和jQuery杂乱绑定DOM的相比已经是进步很多了。

三. backbone源码解析

Backbone源码其实非常简单,直接比对源码即可。简单说明下,总共有8大模块,每个模块都设计的非常规范:

1. Event事件模块:一个事件处理模块

(1) on:注册事件回调函数,如果传人是json对象或空格分隔的多个事件,通过eventsApi方法遍历处理

(2) once:注册只执行一次的回调函数

(3) off:删除事件回调函数,如果没有指明事件名,删除所有事件的回调列表,如果没有指明回调函数,删除该事件的所有回调函数,如果都有指定则删除指定的事件回调函数。如果传人是json对象或空格分隔的多个事件,通过eventsApi方法遍历处理

(4) trigger:触发指定的事件,执行该事件的回调函数列表,如果该对象有all事件,则也会触发all事件。如果传人是json对象或空格分隔的多个事件,通过eventsApi方法遍历处理

(5) listenTo:把事件和回调对象绑定到指定的对象上去。

(6) listenToOnce:同listenTo,只是只会执行一次

(7) stopListening:将对象的事件对象off掉

(8) eventsApi:如果传入的事件是json对象或空格分隔的多个事件,遍历json对象或多个事件,挨个执行指定的操作。

2. Model模型:数据模型,包括数据的设置,获取,后台服务的增删查改等操作

属性:

(1) cid: 当前数据模型的唯一ID,由underscore库生存

(2) attributes:当前数据模型的属性

(3) collection:数据模型所属的集合对象

(4) changed:保存了属性值发生变化的对象集合

方法:

(1) 构造函数: 创建cid,设置collection,如果有parse方法就做parse转换,最后把属性参数设置到本数据模型中,调用initialize方法。

(2) initialize:一个空函数,如果你需要在初始化进行操作可以重写这个方法。

(3) get:或者某个属性的值

(4) set:设置属性值,并将值发生变化了的属性保存到changed对象中去

(5) has:判断数据模型是否包含某个属性

(6) clear:清空数据模型的属性

(7) fetch:通过ajax请求获取模型的属性值

(8) save:保存模型值到后台,如果设置了wait参数,必须要后台返回成功才能更新本地数据

(9) destory:删除数据模型值,如果设置了wait参数,必须要后台返回成功才能更新本地数据

最后将understore库的‘keys‘, ‘values‘, ‘pairs‘, ‘invert‘, ‘pick‘, ‘omit‘, ‘chain‘, ‘isEmpty‘方法添加到Model对象中。

3. Collection集合:Model对象的集合,包括对数据模型的添加,删除,设置,增删查改等操作

属性:

(1) model:Mode对象

(2) models:Model实例集合数组

方法:
(1) 构造函数: 初始化参数,设置model对象,初始化models数组,调用initialize方法,如果传入了model对象数组,清空原数组的依赖关系,将原数组保存到option中去,将新的model数组添加到models数组中去。

(2) initialize:一个空函数,如果你需要在初始化进行操作可以重写这个方法。

(3) add:添加一个Model对象到本集合中去(也就是models数组)

(4) remove:删除一个或多个Model对象,清理_byId对象,清理依赖关系等等

(5) set/get:get获取指定的model对象,set添加model对象到models数组中去,添加新的对象,合并已经存在的对象,删除未列出的对象,

(6) push/pop:添加或删除model对象到models数组尾部

(7) shift/unshift:添加或删除model对象到models数组头部

(8) where:查找指定属性的model对象集合

(9) fetch:从后台读取Model数据

(10) create:创建一个新的model数据,并保存到后台中去

最后将understore库中的所有数组的方法和排序方法添加到Collection对象中去。

4. View模型: 视图模块,对UI中的DOM元素进行操作,并注册浏览器事件

属性:

(1) cid:视图id

(2) el:视图所在的DOM对象

(3)$el:视图所在DOM对象的jQuery对象

(4)id:视图所在DOM对象ID属性

(5)className:视图所在DOM对象className

(6)events:绑定到该视图的对象列表

方法:
(1) 构造函数: 设置cid,初始化option,调用_ensureElement和initialize,其中_ensureElement的作用是确保el是存在的,如果不存在创建一个DOM对象,然后将该对象设置为el,jQuery对象设置为$el,删除el的所有原事件,绑定本视图的event对象列表,如果存在直接将该对象设置为el,后面的逻辑同上。

(2) initialize:一个空函数,如果你需要在初始化进行操作可以重写这个方法。

(3) render:视图渲染逻辑,需要用户重写

(4) remove:删除视图,将删除视图的DOM对象和事件逻辑

(5) setElement:将某个DOM对象设置为el,并绑定事件对象

(6) delegateEvents:为el添加事件对象

(7) undelegateEvents:为el删除事件对象

5. Router路由模块:主要定义和处理路由逻辑,配合History对象可以实现路由规则和url的映射,实现后台和前进按钮的单页效果。例如:

var myRouter = Backbone.Router.extend({
  routes: {
    "help":                 "help",    // #help
    "search/:query":        "search",  // #search/test
    "search/:query/p:page": "search"   // #search/test/p2
  },
  help: function() {
    ...
  },
  search: function(query, page) {
    ...
  }
});

a) help对于help方法

b) search/:query传递一个参数

c) search/:query/p:page 传递两个参数,第二个参数略去p

d) "file/*path"匹配file/后的所有路径

e) "search/:query(/:page)"可以匹配#serach/query和 #serach/query/p1,第一种情况,传入 "query" 到路由对应的动作中去, 第二种情况,传入"query" 和 "p1" 到路由对应的动作中去

属性:

(1) routes:路由列表

方法:
(1) 构造函数: 初始化路由列表,调用_bindRoutes和initialize。

(2) initialize:一个空函数,如果你需要在初始化进行操作可以重写这个方法。

(3) _bindRoutes:将路由列表绑定到实例中去

(4) route:绑定单个路由,首先判断是否是正则表达式,不是的化调用_routeToRegExp转换,首先添加到history对象的handler数组中,然后添加回调函数,回调函数中回调callback,并分析路径中的参数传入到callback中。

(5) execute:执行某个路由的回调,每当路由和其相应的callback匹配时被执行,可以被改写自定义包装或解析路由

(6) navigate:调用history对象的navigate方法,将路由保存到history中去,或者设置trigger:true跳转url,设置replace:true则只跳转,不保存历史。

(7) _routeToRegExp:将字符串转换为路由正则表达式

(8) _extractParameters:分析路径中的参数,比如search/:query/p:num中的query和p

6. History历史记录管理模块:配合路由进行历史记录管理,从而可以实现浏览器的后退前进操作,对于高级浏览器使用History API,对于旧浏览器实现兼容,Backbone实现了3种单页面效果,实现了优雅兼容:

a) pushState:如果设置选项pushState为true并且浏览器支持,则开启该模式,该模式的好处是,既能实现类似ajax方式的无刷新url的更新,也能实现url的变化,对用户来说交互体验最好

b) hasChange:如果设置hasChange选项为true并且浏览器支持,则开启该模式,该模式通过修改url的hash值并且监控hashChange事件来实现单页面效果,可以实现无刷新更新,但是url是通过hash来区分的,体验略差

c) url跳转:如果浏览器既不支持pushState也不支持hasChange,backbone只好使用了url跳转的方式来实现更新,创建一个隐藏的iframe来记录上次的url,创建一个定时器来定时比较两者来实现url的更新,体验不太好

所有这些变化都仅仅是体现在url和history历史记录中去,真正要更新页面的逻辑是路由定义的回调方法。

属性:

(1) handlers:路由规则列表

(2) location:window.location对象

(3) history:window.history对象

方法:
(1) 构造函数: 初始化路由列表,绑定checkUrl方法内部对象为History对象。

(2) atRoot:当前url是否为root根路径

(3) getSearch:获取参数部分

(4) getHash:获取hash部分

(5) getPath:获取路径和所有参数部分,如果option指定了root根路径,则在url中去掉该root

(6) getFragment:获取url中的片段,如果需要pushState或者不支持pushState的浏览器需要刷新url,则调用getPath,否则调用getHash

(7) start:开始监控路由,有两个选项:pushState模式通过pushState地址到history中去,监听popstate事件来实现单页面效果,如果是hasChange模式,通过更新hash和监听hashchange事件来实现,如果是老旧的浏览器,创建一个隐藏iframe记录上次的url,创建一个定时器定时比较当前url和iframe的url是否变化来觉得是否跳转。

(8) stop:停止监控popstate或hashchange方法,如果是iframe模式删除iframe,删除定时器

(9) route:将路由和回调函数添加到handler数组中去

(10) checkUrl:判断当前url是否发生了变化,如果是则调用loadUrl方法

(11) loadUrl:遍历路由列表,找到符合当前url的路由规则并执行回调

(12) navigate:

7. Sync异步请求:ajax请求模块,主要是对ajax的配置,最终调用的是Backbone.ajax方法,如果有jQuery调用jQuery的ajax方法,可以被改写。

8. extend扩展函数:返回一个新的对象child,将实例对象和静态对象拷贝到目标对象中去,通过寄生组合继承方式将child继承自实例对象,通过understore库的extend方法将静态对象拷贝到child中去,然后返回child对象。

时间: 2024-10-24 03:13:07

Backbone设计思路和关键源码分析的相关文章

C++STL内存配置的设计思想与关键源码分析

说明:我认为要读懂STL中allocator部分的源码,并汲取它的思想,至少以下几点知识你要了解:operator new和operator delete.handler函数以及一点模板知识.否则,下面你很可能看不大明白,补充点知识再学习STL源码比较好. 下面会结合关键源码分析C++STL(SGI版本)的内存配置器设计思想.关键词既然是“思想”,所以重点也就呼之欲出了. 1.allocator的简短介绍 我阅读的源码是SGI公司的版本,也是看起来最清楚的版本,各种命名最容易让人看懂.alloc

Backbone.js 0.9.2 源码分析收藏

Backbone 为复杂Javascript应用程序提供模型(models).集合(collections).视图(views)的结构.其中模型用于绑定键值数据和自定义事件:集合附有可枚举函数的丰富API: 视图可以声明事件处理函数,并通过RESRful JSON接口连接到应用程序. 源码分析转之网上它人的备注,特收藏一下,以免方便阅读. // Backbone.js 0.9.2 // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. // Ba

iText生成PDF的关键源码分析

对于iText的学习,基本上都是来源于官网上的例子.这和我学习easyUI.jQuery的途径都是一致的. 这也再次证明了,想用好一个软件,官方提供的例子和文档都是最佳的学习资料. 因为对方是最了解这款软件的人,设计出来的demo当然是最有应用价值的~ 这里吐槽一下,iText的demo真难找,官网上反反复复找了好多次才找到,不知道大家是否和我一样,在这里给大家一个捷径. 下面就拿官网的例子来讲一讲,首先如何生成PDF? /** * 这也就是所有demo中最核心的代码 * Creates a P

MapReduce源码分析之MapTask分析(二)

SpillThread分析 为什么需要Spill 内存大小总是有效,因此在Mapper在处理过程中,数据持续输出到内存中时,必然需要有机制能将内存中的数据换出,合理的刷出到磁盘上.SpillThread就是用来完成这部分工作. SpillThread的线程处理函数只是做一层封装,当索引表中的kvstart和kvend指向一样的索引位置时,会持续处于等待过程,等待外部通知需要触发spill动作,当有spill请求时,会触发StartSpill来唤醒SpillThread线程,进入到sortAndS

Android_WebServices_源码分析

本博文为子墨原创,转载请注明出处! http://blog.csdn.net/zimo2013/article/details/38037989 在Android_WebServices_介绍一文中,简单介绍了WebServices的基础知识,下面主要分析 ksoap2-android-assembly-3.3.0-jar-with-dependencies.jar实现源码. 1.调用WebServices流程 public void getRemoteInfo(String phoneSec)

SpringMVC源码分析-400异常处理流程及解决方法

本文设计SpringMVC异常处理体系源码分析,SpringMVC异常处理相关类的设计模式,实际工作中异常处理的实践. 问题场景 假设我们的SpringMVC应用中有如下控制器: 代码示例-1 @RestController("/order") public class OrderController{ @RequestMapping("/detail") public Object orderDetail(int orderId){ // ... } } 这个控制

mybatis缓存源码分析之浅谈缓存设计

一般我们用到缓存的时候,只知道他很快,很强,还能持久,但是为什么他可以做到这些呢,有人会说这是天赋,遗传的,是的,你想的没错,确实是大佬们在构造这些的时候,赋予他这些能力,那今天我们就来剖析一下,大佬们干了啥,区区缓存就能这么厉害. 去大厂面试的时候,面试官总会喜欢问为什么,一开始,完全搞不懂我就去拧个螺丝,你问我造火箭怎么造我咋知道,后来在工作中遇到各种各样的问题,解决不了的时候,看着身边大佬们一层层点进去看源码分析问题的时候,瞬间觉得这多牛逼啊,或许一开始有动力看源码,了解为什么就是因为这个

Backbone.js源码分析(珍藏版)

源码分析珍藏,方便下次阅读! // Backbone.js 0.9.2 // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. // Backbone may be freely distributed under the MIT license. // For all details and documentation: // http://backbonejs.org (function () { // 创建一个全局对象, 在浏览器中表示为w

Java源码分析——String的设计

Tip:笔者马上毕业了,准备开始Java的进阶学习计划.于是打算先从String类的源码分析入手,作为后面学习的案例.这篇文章寄托着今后进阶系列产出的愿望,希望能坚持下去,不忘初心,让自己保持那份对技术的热爱. 因为学习分析源码,所以借鉴了HollisChuang成神之路的大部分内容,并在此基础上对源码进行了学习,在此感谢. 问题的引入 关于String字符串,对于Java开发者而言,这无疑是一个非常熟悉的类.也正是因为经常使用,其内部代码的设计才值得被深究.所谓知其然,更得知其所以然. 举个例