摘要:這部分比較容易讓人產生疑惑的是循環部分,這個循環有什么用呢舉個例子以上代碼是最簡單的情況,監聽事件,當變化時,打印出在源碼中,當第一次進入后,緊接著被置為,而事件觸發回調函數也不會更改的值,因此再次判斷時條件不成立,內的代碼段只會執行一次。
本文已同步在我的博客
在這個react和vue如日中天、jquery逐漸被大家拋棄的年代,我還是想要來說一說backbone。
16年6月初,在沒有任何前端框架使用經驗、js水平也較一般的情況下,被告知需要在幾個工作日內搭建完成一個后臺管理系統,沒有頁面設計稿、沒有組件庫,一切都是從零開始。當時面臨兩個選擇,backbone和react。雖然我很希望能夠拿react來練手,但是考慮到學習成本和項目時間問題,leader還是建議我使用backbone來完成,就這樣,一直用到差不多現在。雖然到項目后期業務場景越來越復雜,backbone的這套技術棧體現出越來越多的問題,但是對于小型項目來說,我還是認為backbone是個不錯的選擇,而且學習成本低,上手極快~
backbone是個非常輕量的mvc庫,本文將基于backbone的源碼談一談其實現的核心部分,以及其中一些巧妙的思想和代碼實現技巧。
事件機制(Events)事件部分的核心邏輯其實比較簡單,簡化一下可以用如下的偽代碼來表示:
var events = { evt1: handlers1, evt2: handlers2, ... } //注冊事件 function on(name, callback) { const handlers = events[name] || (events[name] = []); handles.push(callback); } //觸發事件 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]; }
當然了,以上寫法有很多細節的地方沒有加入進來,比如上下文綁定、對多種傳參方式的支持、觸發事件時對事件處理器傳參的處理等等。
我們知道,對于MVC來說,M(模型)的變化會反映在V(視圖)上,實際上是視圖監聽了模型的變化,再根據模型去更新自身的狀態,這當中最重要的一個功能就是監聽(listen)。該功能也是由Events部分實現的,包括:listenTo、stopListening等
listenTo和on類似,都是監聽一個事件,只不過listenTo是監聽其他對象的對應事件,而on是監聽自身的對應事件。stopListening同理。比如:
a.on("testevent", function(){ alert("1"); }); a.trigger("testevent");
如果其他對象希望監聽a的testevent事件呢?則可以通過listenTo來實現:
b.listenTo(a, "testevent", function() { alert("catch a"s testevent"); })
其中第一個參數為要監聽的對象,第二個參數為事件名稱
當調用on方法的時候,會為對象自身創建一個_event屬性;而調用listenTo方法時,會為監聽對象創建_event屬性,同時為了記錄監聽者,被監聽對象還會創建一個_listeners屬性:
a.on("testevent", handlers1);
a會變成:
{ _events: { testevent: [handlers1] }, on: function() { //... } ... }
當有其他對象監聽a時,如:
b.listenTo(a, "testevent", handlers2);
a會變成:
{ _events: { testevent: [handlers1, handlers2] }, _listeners: b, on: function() { //... } ... }
在事件機制的實現部分,除了核心邏輯之外,在對一些方法的使用上,也很考究。為了綁定函數執行的上下文,我們經常會使用apply,call這些方法,而源碼中多次提到apply的執行效率要低一些,因此,有這樣的實現:
// 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; } };
有關為什么call比apply的效率更高的解釋可以參考這篇文章
模型(Model)model用于維護數據,其中最關鍵的是對數據的更新部分,即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數據的時候,根據數據變化的部分,使用trigger方法觸發相應的事件;在set數據時,如果不希望觸發change事件,可以設置silent為true。
這部分比較容易讓人產生疑惑的是while循環部分,這個while循環有什么用呢?舉個例子:
new Model.on("change", function() { console.log("model change"); }).set({ a: 1 });
以上代碼是最簡單的情況,監聽change事件,當model變化時,打印出model change
在源碼中,當第一次進入while后,緊接著this._pending被置為false,而事件觸發回調函數也不會更改this._pending的值,因此再次判斷時條件不成立,while內的代碼段只會執行一次。
但是實際情況往往不是這么簡單,如代碼注釋中所說,有可能會有嵌套的情況,比如:
new Model.on("change", function() { this.set({ b: 1 }) }).set({ a: 1 });
在這種情況下,第一次trigger觸發change的回調函數中,又再次對model進行了更新操作,
this.set({ b: 1 })
每次set時,會更新this._pending為true,這樣當set b后,就會再次進入while內,觸發change事件。而如果沒有使用while循環的話,對b屬性更新的操作就無法觸發change事件,導致其監聽者到無法根據最新的數據更新自身狀態。
視圖(View)View部分的實現比較簡單,其中最主要的是events部分,通常在一個View中,都會綁定一些dom事件,比如:
{ "click .preview-btn": "preview", "click .save-btn": "save" }
主要有兩點需要說明:
backbone中是采用的事件委托的方式綁定事件,因此,一些不冒泡的事件,比如scroll,是無法通過這樣的方式綁定的
回調函數會保持正確的this指向。backbone內部進行了處理
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; }結語
以上部分介紹了backbone中最核心部分的實現機制。可以看到其實現非常的簡單,但是對于小型項目來說,確實可以幫我們做一些對數據的維護和管理工作,提高開發效率。但是隨著業務逐漸復雜,會越來越發現,backbone所能做的實現有限,而對于數據維護部分也非常不方便,尤其是需要是對多個模塊間的通信和數據維護問題。后續我會結合在復雜業務中的使用談一談backbone的缺點,以及更優的框架能帶來的便利。
說句題外話,雖然去年由于時間原因選擇了backbone,這一年基本沒有在復雜業務場景中使用react技術棧,都是自己做個小demo練手。但是也正是因為有了使用backbone去寫復雜業務的經歷,在數據維護上和模塊間通信上非常麻煩,以及backbone渲染dom時直接全部更新的會導致的頁面渲染性能問題,才更讓我感覺react + redux的美好。知其然,還需知其所以然啊~ ~
不然我覺得我可能會一直疑惑為什么要用一套這么復雜的技術棧,異步請求這塊寫起來還那么麻煩。這么看,壞事也算是好事了吧~~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92740.html
摘要:如果你并沒有學習過的語法結構,請前往訪問教程接下來,我們在定義組件時設置一個屬性,并返回一個鏈接到屬性內的函數的值之后我們需要在屬性里面寫上函數。 今天來教大家如何使用 ale.js 制作一個小而美的表格編輯器,首先先上 gif: showImg(https://oscimg.oschina.net/oscnet/9cd352e5b3b5eca86b3b8832330899bfac5....
摘要:更好用更強大的筆記本本地翻譯神器解壓縮軟件程序文件快捷神器搜索本地文件神器像瀏覽器一樣打開文件夾記錄每次復制內容黏貼時可選擇復制黏貼神器程序文件快捷神器火螢醬程序文件快捷神器多窗口資源管理器干凈的射手播放器網絡監控控件錄屏軟件錄屏軟件更 Notepad++ 更好用更強大的筆記本 QTranslate 本地翻譯神器 7-zip 解壓縮軟件 Wox 程序/文件/快捷 神器 1! Ever...
摘要:,大家好,好久不賤呢最近因為看了一些的小說,整個人都比較致郁就在昨天,我用了一天的時間寫了,又一個小而美的前端框架可能你覺得,有了和,沒必要再寫一個了我覺得我還是想想辦法尋找一下它的存在感吧先看的組件化方案最先看到的應該是。 halo,大家好,好久不賤呢! 最近因為看了一些 be 的小說,整個人都比較致郁::>__+ {state.count--}}>- ...
摘要:,大家好,好久不賤呢最近因為看了一些的小說,整個人都比較致郁就在昨天,我用了一天的時間寫了,又一個小而美的前端框架可能你覺得,有了和,沒必要再寫一個了我覺得我還是想想辦法尋找一下它的存在感吧先看的組件化方案最先看到的應該是。 halo,大家好,好久不賤呢! 最近因為看了一些 be 的小說,整個人都比較致郁::>__+ {state.count--}}>- ...
閱讀 966·2022-06-21 15:13
閱讀 1853·2021-10-20 13:48
閱讀 1035·2021-09-22 15:47
閱讀 1369·2019-08-30 15:55
閱讀 3125·2019-08-30 15:53
閱讀 523·2019-08-29 12:33
閱讀 717·2019-08-28 18:15
閱讀 3464·2019-08-26 13:58