Backbone前端框架核心及源碼解析
一、 什么是Backbone
在前端的發(fā)展道路中,前端框架元老之一jQuery對繁瑣的DOM操作進(jìn)行了封裝,提供了鏈?zhǔn)秸{(diào)用、各類選擇器,屏蔽了不同瀏覽器寫法的差異性,但是前端開發(fā)過程中依然存在作用域污染、代碼復(fù)用度低、冗余度高、數(shù)據(jù)和事件綁定煩瑣等痛點(diǎn)。
5年后,Backbone橫空出世,通過與Underscore、Require、Handlebar的整合,提供了一個輕量和友好的前端開發(fā)解決方案,其諸多設(shè)計(jì)思想對于后續(xù)的現(xiàn)代化前端框架發(fā)展起到了舉足輕重的作用,堪稱現(xiàn)代前端框架的基石。
通過對Backbone前端框架的學(xué)習(xí),讓我們領(lǐng)略其獨(dú)特的設(shè)計(jì)思想。
二、 核心架構(gòu)
按照MVC框架的定義,MVC是用來將應(yīng)用程序分為三個主要邏輯組件的架構(gòu)模式:模型,視圖和控制器。這些組件被用來處理一個面向應(yīng)用的特定開發(fā)。 MVC是最常用的行業(yè)標(biāo)準(zhǔn)的Web開發(fā)框架,以創(chuàng)建可擴(kuò)展的項(xiàng)目之一。 Backbone.js為復(fù)雜WEB應(yīng)用程序提供模型(models)、集合(collections)、視圖(views)的結(jié)構(gòu)。
? 其中模型用于綁定鍵值數(shù)據(jù),并通過RESRful JSON接口連接到應(yīng)用程序;
? 視圖用于UI界面渲染,可以聲明自定義事件,通過監(jiān)聽模型和集合的變化執(zhí)行相應(yīng)的回調(diào)(如執(zhí)行渲染)。

如圖所示,當(dāng)用戶與視圖層產(chǎn)生交互時,控制層監(jiān)聽變化,負(fù)責(zé)與數(shù)據(jù)層進(jìn)行數(shù)據(jù)交互,觸發(fā)數(shù)據(jù)Change事件,從而通知視圖層重新渲染,以實(shí)現(xiàn)UI界面更新。更進(jìn)一步,當(dāng)數(shù)據(jù)層發(fā)生變化時,由Backbone提供了數(shù)據(jù)層和服務(wù)器數(shù)據(jù)共享同步的能力。
其設(shè)計(jì)思想主要包含以下幾點(diǎn):
?數(shù)據(jù)綁定(依賴渲染模板引擎)、事件驅(qū)動(依賴Events)
?視圖組件化,并且組件有了生命周期的概念
?前端路由配置化,實(shí)現(xiàn)頁面局部刷新
這些創(chuàng)新的思想,在現(xiàn)代前端框架中進(jìn)一步得到了繼承和發(fā)揚(yáng)。
三、 部分源碼解析
Backbone極度輕量,編譯后僅有幾kb,貫穿其中的是大量的設(shè)計(jì)模式:工廠模式、觀察者模式、迭代器模式、適配器模式……,代碼流暢、實(shí)現(xiàn)過程比較優(yōu)雅。按照功能拆分為了Events、Model、Collection、Router、History、View等若干模塊,這里摘取了部分精彩源碼進(jìn)行了解析,相信對我們的日常代碼開發(fā)也有一定指導(dǎo)作用:
(1)迭代器
EventsApi起到一個迭代器分流的作用,對多個事件進(jìn)行解析拆分,設(shè)計(jì)的非常經(jīng)典,執(zhí)行時以下用法都是合法的:
?用法一:傳入一個名稱和回調(diào)函數(shù)的對象
modal.on({
"change": change_callback,
"remove": remove_callback
})
?用法二:使用空格分割的多個事件名稱綁定到同一個回調(diào)函數(shù)上
model.on("change remove", common_callback)
實(shí)現(xiàn)如下:
var eventsApi = function(iteratee, events, name, callback, opts) {
var i = 0, names;
if(name && typeof name === 'object') {
// 處理第一種用法
if(callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
for(names = _.keys(names); i < names.length; i++) events = eventsApi(iteratee, events, names[i], name[names[i]], opts);
} else if(name && eventSplitter.test(name)) {
// 處理第二種用法
for(names = name.split(eventSplitter); i < names.length; i++) events = iteratee(events, names[i], callback, opts);
} else {
events = iteratee(events, name, callback, opts);
}
return events;
}
(2)監(jiān)聽器
用于一個對象監(jiān)聽另外一個對象的事件,例如,在A對象上監(jiān)聽在B對象上發(fā)生的事件,并且執(zhí)行A的回調(diào)函數(shù):
A.listenTo(B, "b", callback)
實(shí)際上這個功能用B對象來監(jiān)聽也可以實(shí)現(xiàn):
B.on("b", callback, A)
這么做的好處是,方便對A創(chuàng)建、銷毀邏輯的代碼聚合,并且對B的侵入程度較小。實(shí)現(xiàn)如下:
Events.listenTo = function(obj, name, callback) {
if(!obj) return this;
var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
// 當(dāng)前對象的所有監(jiān)聽對象
var listeningTo = this._listeningTo || (this._listeningTo = {});
var listening = listeningTo[id];
if(!listening) {
// 創(chuàng)建自身監(jiān)聽id
var thisId = this._listenId || (this._listenId = _.uniqueId('l'));
listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0};
}
// 執(zhí)行對象綁定
internalOn(obj, name, callback, this, listening);
return this;
}
(3)Model值set
通過option-flags兼容賦值、更新、刪除等操作,這么做的好處是融合公共邏輯,簡化代碼邏輯和對外暴露api。實(shí)現(xiàn)如下:
set: function(key, val, options) {
if(key == null) return this;
// 支持兩種賦值方式: 對象或者 key\value
var attrs;
if(typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
options || (options = {});
……
var unset = options.unset;
var silent = options.silent;
var changes = [];
var changing = this._changing; // 處理嵌套set
this._changing = true;
if(!changing) {
// 存儲變更前的狀態(tài)快照
this._previousAttributes = _.clone(this.attributes);
this.changed = {};
}
var current = this.attributes;
var changed = this.changed;
var prev = this._previousAttributes;
for(var attr in attrs) {
val = attrs[attr];
if(!_.isEqual(current[attr], val)) changes.push(attr);
// changed只存儲本次變化的key
if(!_.isEqual(prev[attr], val)) {
changed[attr] = val;
} else {
delete changed[attr]
}
unset ? delete current[attr] : (current[attr] = val)
}
if(!silent) {
if(changes.length) this._pending = options;
for(var i=0; i<changes.length; i++) {
// 觸發(fā) change:attr 事件
this.trigger('change:' + changes[i], this, current[changes[i]], options);
}
}
if(changing) return this;
if(!silent) {
// 處理遞歸change場景
while(this._pending) {
options = this._pending;
this._pending = false;
this.trigger('change', this, options);
}
}
this._pending = false;
this._changing = false;
return this;
}
四、 不足(對比react、vue)
對比現(xiàn)代前端框架,由于Backbone本身比較輕量,對一些內(nèi)容細(xì)節(jié)處理不夠細(xì)膩,主要體現(xiàn)在:
?視圖和數(shù)據(jù)的交互關(guān)系需要自己分類編寫邏輯,需要編寫較多的監(jiān)聽器
?監(jiān)聽器數(shù)量較大,需要手動銷毀,維護(hù)成本較高
?視圖樹的二次渲染僅能實(shí)現(xiàn)組件整體替換,并非增量更新,存在性能損失
?路由切換需要自己處理頁面更新邏輯
五、為什么選擇Backbone
看到這里,你可能有些疑問,既然Backbone存在這些缺陷,那么現(xiàn)在學(xué)習(xí)Backbone還有什么意義呢?
首先,對于服務(wù)端開發(fā)人員,Backbone底層依賴underscore/lodash、jQuery/Zepto,目前依然有很多基于Jquery和Velocity的項(xiàng)目需要維護(hù),會jQuery就會Backbone,學(xué)習(xí)成本低;通過Backbone能夠?qū)W習(xí)用數(shù)據(jù)去驅(qū)動View更新,優(yōu)化jQuery的寫法;Backbone面對對象編程,符合Java開發(fā)習(xí)慣。
其次,對于前端開發(fā)人員,能夠?qū)W習(xí)其模塊化封裝庫類函數(shù),提升編程技藝。Backbone的組件化開發(fā),和現(xiàn)代前端框架有很多共通之處,能夠深入理解其演化歷史。
以上就是Backbone前端框架核心及源碼解析的詳細(xì)內(nèi)容,更多關(guān)于Backbone前端框架的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
VUE 實(shí)現(xiàn)動態(tài)給對象增加屬性,并觸發(fā)視圖更新操作示例
這篇文章主要介紹了VUE 實(shí)現(xiàn)動態(tài)給對象增加屬性,并觸發(fā)視圖更新操作,涉及vue.js對象屬性動態(tài)操作及視圖更新相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-11-11
Vue mixin實(shí)現(xiàn)組件功能復(fù)用示例詳解
這篇文章主要為大家介紹了Vue mixin實(shí)現(xiàn)組件功能復(fù)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
el-descriptions引入代碼中l(wèi)abel不生效問題及解決
這篇文章主要介紹了el-descriptions引入代碼中l(wèi)abel不生效問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
在vue項(xiàng)目中(本地)使用iconfont字體圖標(biāo)的三種方式總結(jié)
這篇文章主要介紹了在vue項(xiàng)目中(本地)使用iconfont字體圖標(biāo)的三種方式總結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09
Delete `,` 如何解決(vue eslint與prettier沖突)
這篇文章主要介紹了Delete `,` 如何解決(vue eslint與prettier沖突)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10
Vue2中Element?DatePicker組件設(shè)置默認(rèn)日期及控制日期范圍
后臺項(xiàng)目想使用時間選擇器選擇一段時間進(jìn)行數(shù)據(jù)篩選,所以下面這篇文章主要給大家介紹了關(guān)于Vue2中Element?DatePicker組件設(shè)置默認(rèn)日期及控制日期范圍的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11
Vue-router路由判斷頁面未登錄跳轉(zhuǎn)到登錄頁面的實(shí)例
下面小編就為大家?guī)硪黄猇ue-router路由判斷頁面未登錄跳轉(zhuǎn)到登錄頁面的實(shí)例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
解決vue 更改計(jì)算屬性后select選中值不更改的問題
下面小編就為大家分享一篇解決vue 更改計(jì)算屬性后select選中值不更改的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-03-03
rollup3.x+vue2打包組件的實(shí)現(xiàn)
本文主要介紹了rollup3.x+vue2打包組件的實(shí)現(xiàn),詳細(xì)的介紹了打包會存在的問題,包版本的問題,babel 轉(zhuǎn)換jsx等問題,具有一定的參考價值,感興趣的可以了解一下2023-03-03

