一. Backbone的江湖地位:

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

1. 无前端:最早的网页就是HTML,还只是静态页面。世界上第一个网页:

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只是UI层面的框架,并没有包含逻辑层面,另外两个问题还需要解决:代码组织结构和组件通信。MVC模式也是为了解决前端复杂度问题,但是仍然不是最佳方案,试想以下的情景:

通过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) 需求变化时,由于维护成本居高不下,更新需求的开发效率底下。

e) 单页面开发比较繁琐

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

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

2. 他有什么优点?

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

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

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

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

(5)单页面开发更加方便

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的映射,实现后台和前进按钮的单页效果,Router路由模块的主要作用就是记录用户定义的路由规则,作为History的对外接口。例如:

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

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

所有这些变化都仅仅是体现在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: 执行单页面的历史记录管理,如果是pushState模式,则调用replaceState或pushState来管理历史,如果hasChange模式,则通过更新url的hash管理历史,否则直接跳转url,通过浏览器自己管理历史

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

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

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

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

    说明:我认为要读懂STL中allocator部分的源码,并汲取它的思想,至少以下几点知识你要了解:operator new和operator delete.handler函数以及一点模板知识.否则,下 ...

  2. Backbone.js 0.9.2 源码分析收藏

    Backbone 为复杂Javascript应用程序提供模型(models).集合(collections).视图(views)的结构.其中模型用于绑定键值数据和自定义事件:集合附有可枚举函数的丰富A ...

  3. .net 平台下, Socket通讯协议中间件设计思路(附源码)

    .net 平台下,实现通讯处理有很多方法(见下表),各有利弊: 序号 实现方式 特点 1 WCF 优点:封装好,方便.缺点:难学,不跨平台 2 RocketMQ,SuperSocket等中间件 优点: ...

  4. PHP与RBAC设计思路讲解与源码

    在说权限管理前,应该先知道权限管理要有哪些功能: (1).用户只能访问,指定的控制器,指定的方法 (2).用户可以存在于多个用户组里 (3).用户组可以选择,指定的控制器,指定的方法 (4).可以添加 ...

  5. spark源码分析以及优化

    第一章.spark源码分析之RDD四种依赖关系 一.RDD四种依赖关系 RDD四种依赖关系,分别是 ShuffleDependency.PrunDependency.RangeDependency和O ...

  6. SOFABolt 源码分析

    SOFABolt 是一个轻量级.高性能.易用的远程通信框架,基于netty4.1,由蚂蚁金服开源. 本系列博客会分析 SOFABolt 的使用姿势,设计方案及详细的源码解析.后续还会分析 SOFABo ...

  7. MyBatis 之 SqlSessionManager 源码分析

    MyBatis 的 4 个基本构成: SqlSessionFactoryBuilder(构造器): 根据配置信息或者代码来生成 SqlSessionFactory(工厂接口) SqlSessionFa ...

  8. Android源码分析(九)-----如何修改Android系统默认时间

    一 : 修改Android系统默认时间 源码路径:frameworks/base/services/java/com/android/server/SystemServer.java 主要变量EARL ...

  9. Android源码分析(八)-----系统启动流程&IPC简述

    一 :系统启动流程图 从下往上依次启动linux kernel -->zygote-->SystemServer-->NativeService-->AndroidServic ...

随机推荐

  1. Celery最佳实践(转)

    原文:http://my.oschina.net/siddontang/blog/284107 英文原文:https://denibertovic.com/posts/celery-best-prac ...

  2. linux 安装libevent

    今天再ubuntu下安装libevent,下载源码 tar -xzvf libevent-1.4.15.tar.gz cd libevent-1.4.15 ./configure make make ...

  3. Drawing Graphs using Dot and Graphviz

    Drawing Graphs using Dot and Graphviz Table of Contents 1. License 2. Introduction 2.1. What is DOT? ...

  4. Java调用Python脚本并获取返回值

    在Java程序中有时需要调用Python的程序,这时可以使用一般的PyFunction来调用python的函数并获得返回值,但是采用这种方法有可能出现一些莫名其妙的错误,比如ImportError.在 ...

  5. OCR技术浅探:Python示例(5)

    文件说明: 1. image.py——图像处理函数,主要是特征提取: 2. model_training.py——训练CNN单字识别模型(需要较高性能的服务器,最好有GPU加速,否则真是慢得要死): ...

  6. OleDb未指定错误

    桌面开发,居然也出这种问题: 1. C#读取Excel“未指定错误” http://www.connectionstrings.com/ http://www.dnetzj.com/Content/2 ...

  7. mysql性能测试--sysbench实践

    mysql性能测试--sysbench实践 Sysbench   业界较为出名的性能测试工具 可以测试磁盘,CPU,数据库 支持多种数据库:oracle,DB2,MYSQL 需要自己下载编译安装 建议 ...

  8. django-from

    构建一个表单 这是一个非常简单的表单.实际应用中,一个表单可能包含几十上百个字段,其中大部分需要预填充,而且我们预料到用户将来回编辑-提交几次才能完成操作. 我们可能需要在表单提交之前,在浏览器端作一 ...

  9. 常微分方程初值问题:单步方法 [MATLAB]

    #先上代码后补笔记# #可以直接复制粘贴调用的MATLAB函数代码!# 1. 朗格-库塔(Runge-Kutta)方法族 目前只实现了四阶Runge-Kutta方法. function [ YMat ...

  10. Linux系统——DNS

    DNS系统的作用1. DNS服务器Internet中,大部分网站.邮件服务等服务器都使用了域名形式的地址,这种地址形式要比使用IP地址形式更加直观,更加容易被用户记住.FQDN格式(完整域名格式):在 ...