摘要:這部分比較容易讓人產(chǎn)生疑惑的是循環(huán)部分,這個(gè)循環(huán)有什么用呢舉個(gè)例子以上代碼是最簡單的情況,監(jiān)聽事件,當(dāng)變化時(shí),打印出在源碼中,當(dāng)?shù)谝淮芜M(jìn)入后,緊接著被置為,而事件觸發(fā)回調(diào)函數(shù)也不會更改的值,因此再次判斷時(shí)條件不成立,內(nèi)的代碼段只會執(zhí)行一次。
本文已同步在我的博客
在這個(gè)react和vue如日中天、jquery逐漸被大家拋棄的年代,我還是想要來說一說backbone。
16年6月初,在沒有任何前端框架使用經(jīng)驗(yàn)、js水平也較一般的情況下,被告知需要在幾個(gè)工作日內(nèi)搭建完成一個(gè)后臺管理系統(tǒng),沒有頁面設(shè)計(jì)稿、沒有組件庫,一切都是從零開始。當(dāng)時(shí)面臨兩個(gè)選擇,backbone和react。雖然我很希望能夠拿react來練手,但是考慮到學(xué)習(xí)成本和項(xiàng)目時(shí)間問題,leader還是建議我使用backbone來完成,就這樣,一直用到差不多現(xiàn)在。雖然到項(xiàng)目后期業(yè)務(wù)場景越來越復(fù)雜,backbone的這套技術(shù)棧體現(xiàn)出越來越多的問題,但是對于小型項(xiàng)目來說,我還是認(rèn)為backbone是個(gè)不錯(cuò)的選擇,而且學(xué)習(xí)成本低,上手極快~
backbone是個(gè)非常輕量的mvc庫,本文將基于backbone的源碼談一談其實(shí)現(xiàn)的核心部分,以及其中一些巧妙的思想和代碼實(shí)現(xiàn)技巧。
事件機(jī)制(Events)事件部分的核心邏輯其實(shí)比較簡單,簡化一下可以用如下的偽代碼來表示:
var events = { evt1: handlers1, evt2: handlers2, ... } //注冊事件 function on(name, callback) { const handlers = events[name] || (events[name] = []); handles.push(callback); } //觸發(fā)事件 function trigger(name) { if (!events[name]) { return; } const handlers = events[name]; for (let i = 0, len = handlers.length; i < len; i++) { handlers[i](); } } //解除綁定 function off(name) { delete events[name]; }
當(dāng)然了,以上寫法有很多細(xì)節(jié)的地方?jīng)]有加入進(jìn)來,比如上下文綁定、對多種傳參方式的支持、觸發(fā)事件時(shí)對事件處理器傳參的處理等等。
我們知道,對于MVC來說,M(模型)的變化會反映在V(視圖)上,實(shí)際上是視圖監(jiān)聽了模型的變化,再根據(jù)模型去更新自身的狀態(tài),這當(dāng)中最重要的一個(gè)功能就是監(jiān)聽(listen)。該功能也是由Events部分實(shí)現(xiàn)的,包括:listenTo、stopListening等
listenTo和on類似,都是監(jiān)聽一個(gè)事件,只不過listenTo是監(jiān)聽其他對象的對應(yīng)事件,而on是監(jiān)聽自身的對應(yīng)事件。stopListening同理。比如:
a.on("testevent", function(){ alert("1"); }); a.trigger("testevent");
如果其他對象希望監(jiān)聽a的testevent事件呢?則可以通過listenTo來實(shí)現(xiàn):
b.listenTo(a, "testevent", function() { alert("catch a"s testevent"); })
其中第一個(gè)參數(shù)為要監(jiān)聽的對象,第二個(gè)參數(shù)為事件名稱
當(dāng)調(diào)用on方法的時(shí)候,會為對象自身創(chuàng)建一個(gè)_event屬性;而調(diào)用listenTo方法時(shí),會為監(jiān)聽對象創(chuàng)建_event屬性,同時(shí)為了記錄監(jiān)聽者,被監(jiān)聽對象還會創(chuàng)建一個(gè)_listeners屬性:
a.on("testevent", handlers1);
a會變成:
{ _events: { testevent: [handlers1] }, on: function() { //... } ... }
當(dāng)有其他對象監(jiān)聽a時(shí),如:
b.listenTo(a, "testevent", handlers2);
a會變成:
{ _events: { testevent: [handlers1, handlers2] }, _listeners: b, on: function() { //... } ... }
在事件機(jī)制的實(shí)現(xiàn)部分,除了核心邏輯之外,在對一些方法的使用上,也很考究。為了綁定函數(shù)執(zhí)行的上下文,我們經(jīng)常會使用apply,call這些方法,而源碼中多次提到apply的執(zhí)行效率要低一些,因此,有這樣的實(shí)現(xiàn):
// A difficult-to-believe, but optimized internal dispatch function for // triggering events. Tries to keep the usual cases speedy (most internal // Backbone events have 3 arguments). var triggerEvents = function(events, args) { var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; switch (args.length) { case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; } };
有關(guān)為什么call比apply的效率更高的解釋可以參考這篇文章
模型(Model)model用于維護(hù)數(shù)據(jù),其中最關(guān)鍵的是對數(shù)據(jù)的更新部分,即set
// Trigger all relevant attribute changes. if (!silent) { if (changes.length) this._pending = options; for (var i = 0; i < changes.length; i++) { this.trigger("change:" + changes[i], this, current[changes[i]], options); } } // You might be wondering why there"s a `while` loop here. Changes can // be recursively nested within `"change"` events. if (changing) return this; if (!silent) { while (this._pending) { options = this._pending; this._pending = false; this.trigger("change", this, options); } } this._pending = false; this._changing = false; return this;
每次set數(shù)據(jù)的時(shí)候,根據(jù)數(shù)據(jù)變化的部分,使用trigger方法觸發(fā)相應(yīng)的事件;在set數(shù)據(jù)時(shí),如果不希望觸發(fā)change事件,可以設(shè)置silent為true。
這部分比較容易讓人產(chǎn)生疑惑的是while循環(huán)部分,這個(gè)while循環(huán)有什么用呢?舉個(gè)例子:
new Model.on("change", function() { console.log("model change"); }).set({ a: 1 });
以上代碼是最簡單的情況,監(jiān)聽change事件,當(dāng)model變化時(shí),打印出model change
在源碼中,當(dāng)?shù)谝淮芜M(jìn)入while后,緊接著this._pending被置為false,而事件觸發(fā)回調(diào)函數(shù)也不會更改this._pending的值,因此再次判斷時(shí)條件不成立,while內(nèi)的代碼段只會執(zhí)行一次。
但是實(shí)際情況往往不是這么簡單,如代碼注釋中所說,有可能會有嵌套的情況,比如:
new Model.on("change", function() { this.set({ b: 1 }) }).set({ a: 1 });
在這種情況下,第一次trigger觸發(fā)change的回調(diào)函數(shù)中,又再次對model進(jìn)行了更新操作,
this.set({ b: 1 })
每次set時(shí),會更新this._pending為true,這樣當(dāng)set b后,就會再次進(jìn)入while內(nèi),觸發(fā)change事件。而如果沒有使用while循環(huán)的話,對b屬性更新的操作就無法觸發(fā)change事件,導(dǎo)致其監(jiān)聽者到無法根據(jù)最新的數(shù)據(jù)更新自身狀態(tài)。
視圖(View)View部分的實(shí)現(xiàn)比較簡單,其中最主要的是events部分,通常在一個(gè)View中,都會綁定一些dom事件,比如:
{ "click .preview-btn": "preview", "click .save-btn": "save" }
主要有兩點(diǎn)需要說明:
backbone中是采用的事件委托的方式綁定事件,因此,一些不冒泡的事件,比如scroll,是無法通過這樣的方式綁定的
回調(diào)函數(shù)會保持正確的this指向。backbone內(nèi)部進(jìn)行了處理
delegateEvents: function(events) { events || (events = _.result(this, "events")); if (!events) return this; this.undelegateEvents(); for (var key in events) { var method = events[key]; if (!_.isFunction(method)) method = this[method]; if (!method) continue; var match = key.match(delegateEventSplitter); this.delegate(match[1], match[2], _.bind(method, this)); } return this; }結(jié)語
以上部分介紹了backbone中最核心部分的實(shí)現(xiàn)機(jī)制??梢钥吹狡鋵?shí)現(xiàn)非常的簡單,但是對于小型項(xiàng)目來說,確實(shí)可以幫我們做一些對數(shù)據(jù)的維護(hù)和管理工作,提高開發(fā)效率。但是隨著業(yè)務(wù)逐漸復(fù)雜,會越來越發(fā)現(xiàn),backbone所能做的實(shí)現(xiàn)有限,而對于數(shù)據(jù)維護(hù)部分也非常不方便,尤其是需要是對多個(gè)模塊間的通信和數(shù)據(jù)維護(hù)問題。后續(xù)我會結(jié)合在復(fù)雜業(yè)務(wù)中的使用談一談backbone的缺點(diǎn),以及更優(yōu)的框架能帶來的便利。
說句題外話,雖然去年由于時(shí)間原因選擇了backbone,這一年基本沒有在復(fù)雜業(yè)務(wù)場景中使用react技術(shù)棧,都是自己做個(gè)小demo練手。但是也正是因?yàn)橛辛耸褂?strong>backbone去寫復(fù)雜業(yè)務(wù)的經(jīng)歷,在數(shù)據(jù)維護(hù)上和模塊間通信上非常麻煩,以及backbone渲染dom時(shí)直接全部更新的會導(dǎo)致的頁面渲染性能問題,才更讓我感覺react + redux的美好。知其然,還需知其所以然啊~ ~
不然我覺得我可能會一直疑惑為什么要用一套這么復(fù)雜的技術(shù)棧,異步請求這塊寫起來還那么麻煩。這么看,壞事也算是好事了吧~~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/92740.html
摘要:如果你并沒有學(xué)習(xí)過的語法結(jié)構(gòu),請前往訪問教程接下來,我們在定義組件時(shí)設(shè)置一個(gè)屬性,并返回一個(gè)鏈接到屬性內(nèi)的函數(shù)的值之后我們需要在屬性里面寫上函數(shù)。 今天來教大家如何使用 ale.js 制作一個(gè)小而美的表格編輯器,首先先上 gif: showImg(https://oscimg.oschina.net/oscnet/9cd352e5b3b5eca86b3b8832330899bfac5....
摘要:更好用更強(qiáng)大的筆記本本地翻譯神器解壓縮軟件程序文件快捷神器搜索本地文件神器像瀏覽器一樣打開文件夾記錄每次復(fù)制內(nèi)容黏貼時(shí)可選擇復(fù)制黏貼神器程序文件快捷神器火螢醬程序文件快捷神器多窗口資源管理器干凈的射手播放器網(wǎng)絡(luò)監(jiān)控控件錄屏軟件錄屏軟件更 Notepad++ 更好用更強(qiáng)大的筆記本 QTranslate 本地翻譯神器 7-zip 解壓縮軟件 Wox 程序/文件/快捷 神器 1! Ever...
摘要:,大家好,好久不賤呢最近因?yàn)榭戳艘恍┑男≌f,整個(gè)人都比較致郁就在昨天,我用了一天的時(shí)間寫了,又一個(gè)小而美的前端框架可能你覺得,有了和,沒必要再寫一個(gè)了我覺得我還是想想辦法尋找一下它的存在感吧先看的組件化方案最先看到的應(yīng)該是。 halo,大家好,好久不賤呢! 最近因?yàn)榭戳艘恍?be 的小說,整個(gè)人都比較致郁::>__+ {state.count--}}>- ...
摘要:,大家好,好久不賤呢最近因?yàn)榭戳艘恍┑男≌f,整個(gè)人都比較致郁就在昨天,我用了一天的時(shí)間寫了,又一個(gè)小而美的前端框架可能你覺得,有了和,沒必要再寫一個(gè)了我覺得我還是想想辦法尋找一下它的存在感吧先看的組件化方案最先看到的應(yīng)該是。 halo,大家好,好久不賤呢! 最近因?yàn)榭戳艘恍?be 的小說,整個(gè)人都比較致郁::>__+ {state.count--}}>- ...
閱讀 975·2022-06-21 15:13
閱讀 1857·2021-10-20 13:48
閱讀 1044·2021-09-22 15:47
閱讀 1376·2019-08-30 15:55
閱讀 3132·2019-08-30 15:53
閱讀 528·2019-08-29 12:33
閱讀 724·2019-08-28 18:15
閱讀 3472·2019-08-26 13:58