Backbone源碼解讀(三)

注意:強烈建議一邊閱讀源碼一邊閱讀本文。html

終於到了backbone源碼解讀的最後一篇,這一篇和前面幾篇時間上有必定的間隔(由於要回學校有一堆亂七八糟的事...)。在這一篇裏面會講解Bakcbonesync & router & histrorysync比較簡單,可是路由的部分就比較複雜了。我的以爲是整個backbone源碼裏面最很差懂的一個部分,這個部分也使得backbone能夠方便實現能夠「返回」的單頁面應用。我的以爲這個部分其實並無很MVC有很密切的關係,可是它很是重要。讀過源碼就會發現,其實這一個部分與其餘的模塊(ModelCollection & View)相對獨立。若是不但願使用backbone但但願能用到這個路由系統的話估計拆出來難度也不會很大。node

整體來講路由模塊由RouterHistrory組成,而Router事實上能夠當作是對History的一個封裝。用戶直接操做的部分時Router,相關函數的處理(路由匹配時調用的函數)也是在Router中完成。History主要是處理一個更加棘手的問題,就是有關連接的問題。這裏面有關於跨瀏覽器的解決方案是很是值得學習的。jquery

1. Router

Router是對History的封裝,也是給用戶定義路由的接口。通常來講,用戶在使用Router的時候會定義一個routers的對象,裏面是想關路由與處理函數組成的key-valueRouter代碼中主要作兩件事,一件是對正則表達式的操做(能夠看見裏面不少使人痛苦的正則表達式),另外一件事就是事件相關的綁定了。Router的相關代碼很少,寫得比較精簡。git

1.1 函數綁定與執行

Router裏面定義了兩種方法來定義路由事件,一個方法是用戶定義routers,經過初始化調用_bindRoutes函數;一個方法是用自帶的route函數。其實兩個方法本質上都是調用了route函數_bindRoutes裏面實際上也是經過循環來調用route函數。所以路由函數重點在於route函數。github

route函數裏面,一開始是進行一些參數的整理。而後就是調用了History模塊的route函數,把正則(匹配參數用的)和一個回調函數傳了進去。在回調函數裏面就是作 執行函數——觸發router的事件——觸發history的事件這幾個步驟,在Historyroute函數裏面,只是簡單的插入地把keycallback組成對象插入handlers數組裏面而已。ajax

1.2. 正則表達式轉化與使用

Router函數裏面最難懂也很是重要的部分是格式的轉換。在Router裏面有兩個重要的函數_routeToRegExp函數和_extractParameters兩個,這兩個函數與正則密切相關。正則表達式

_extractParameters函數的做用是利用正則表達式,把傳進來的url片斷fragment分割成片斷存進數組當中。這些片斷是真實的已經匹配出來的參數,在route函數裏面會把這些參數傳給用戶定義的函數裏面,供用戶使用。segmentfault

_routeToRegExp函數是一個簡單,但要徹底理解很難的函數。這個函數的做用就是返回一個RegExp對象,經過這一個對象來匹配當前的連接,而後從中獲得參數。進入這個函數以後會經過字符串的replace函數,匹配出路由的是哪幾(或一)種狀況,而且替代成能夠捕獲參數的正則字符串。好比說把路由定義裏的/:page或者*fragment這樣的字符串經過事先定義好的幾個正則匹配到,而後換成帶()的能夠捕獲的形式,而後在建立正則去捕獲真正須要的常數。api

2. History

backbone中對於History模塊的使用是經過用構造方式調用(new)返回一個可使用prototype方法的對象來實現的。Backbone.history = new History; 這個模塊很是重要,並且在整個backbone裏面能夠說是最難徹底讀懂的。下面我會從三個方面來說:一個方面是有關於路徑格式處理的問題,在這方面也有不少和正則表達式相關的函數;另外一個方面是最關鍵的一個方面,就是History檢測瀏覽器來使用不一樣的路由控制方式;最後一個方面就是經過具體的函數來說解它是如何實現第二點各方面所說的控制的。數組

History從接口的角度來講有start函數做爲初始化的設置,還有經過Router模塊封裝的navigate方法。Router裏面的不少處理須要調用到這個模塊的方法。

History事實上也是對location/history必定程度上的封裝。不少時候是經過location模塊來讀取匹配,經過history的一些方法來進行路由控制。

2.1 路徑格式處理

2.1.1 root

用戶能夠設置root,做爲根路徑。這個根路徑在模塊中有一些判斷和處理的地方。比方說肯定當前是否在根路徑,或者在當前URL提取出相應的錨點等等都須要用到root這個內部變量。

2.1.2 getFragment

在這個函數裏面咱們能夠看到URL的格式分爲了兩種。一種是hash方式,一種是search方式(主要是兼容較老的瀏覽器)。在這裏經過判斷來進行瀏覽器能力檢測。對於大部分現代瀏覽器來講,事實上大都是使用hash方式獲取錨點#後面的URL片斷。

2.2 路由控制的三種方法(核心)

2.2.1 onhashchange方式

這種方式是經過監聽'hashchange'事件,而後觸發事件,用location.hash.replace方法來改變路由。

2.2.2 pushstate方式

這種方式是最爲推薦的HTML5方式。使用historypushState方法修改history裏的記錄,而後也能夠經過監聽popstate來觸發一些相關的事件。

2.2.3 iframe方式

這是一個很是巧妙可是從某種程度上很是「醜陋」的方法。醜陋是在它比較吃性能,一方面它有不少dom的操做來設置iframe,最重要的方面是它還用了定時器每隔一小段時間就檢測,而後就觸發函數,判斷是否改變等等。插入一個空的iframe(通過屬性設置)的做用在這裏是存儲hash的值和存儲hash改變記錄。在這裏我遇到了一個問題:存儲hash的值徹底能夠經過一個全局變量來完成,爲何要大費周折建立一個iframe呢?下面是我的的一些猜想:

Opening and closing the iframe tricks IE7 and earlier to push a history entry on hash-tag change.

這是源碼中的一句註釋。用iframe的理由多是爲了經過開關iframe來存儲記錄。說實話具體是什麼原理還不清楚,若是有人瞭解的話歡迎指教~

// 開關`iframe`
iWindow.document.open();
iWindow.document.close();

2.3 實現(start, navigate & 事件)

2.3.1 start

start主要作以下操做:

  • 進入start函數以後會把started設置爲true防止重複出發。

  • 設置各類參數,用於後期判斷使用哪種路由控制方式。

  • 若是有hashchange事件,但沒有pushState方法,就用location.replace方法來改變路由。若是二者都有就調用navigate函數,裏面能夠經過pushState改變並記錄路由。若是二者都沒有就設置iframe並啓動,經過設置iframehash參數來改變路由。

  • 綁定事件,用hashChange方法的綁定hashchange事件,用pushState方法的綁定popstate事件,用iframe的使用setInterval來監聽。

2.3.2 navigate

進入函數以後首先是進行「組合」,「組合」出url。這個過程須要有rootfragment,後者須要調用getFragment函數,前者須要根據是path仍是hash來對root進行處理。若是是hash就不須要加/,若是是path就要加/。解碼後判斷當前的this.fragment和有沒有發生變化,沒有無論,有就更新。

根據瀏覽器使用不一樣的方法。注意這裏使用的判斷的依據是在start函數裏面就定義好的。

  • 若是有pushState或者replaceState就用;

  • 若是有hashchange就僅僅只調用_upadateHash,傳入當前的location bom對象,裏面用了location.replace,更新當前的href或者hash

  • 若是沒有hashchange就須要把當前locationiframe.location對象分別傳入_updateHash,而後更新當前href或者hash

    還有一個須要注意的是是否replace,這是一個傳入參數,判斷時候要影響history

2.3.3 事件

關於路由觸發事件是經過兩個函數來完成的,它們分別是checkUrlloadUrl, 前者會檢測路由是否發生了改變,若是改變了就會觸發navigate函數並調用loadUrl函數,然後者會經過路由片斷來找到handlers相關的事件函數來觸發。這就實現了用戶在routes對象裏面設置的事件了。

3. Sync

最後來個簡單的Sync的講解吧~有關ajax的部分在backbone中實際上是經過Backbone.ajax函數來代理jquery或者其餘能夠發起ajax的庫的。而Sync函數事實上主要的工做就是部署ajax參數,最後調用這個Backbone.ajax發起請求。經過源碼能夠看到,其中爲params設置了type, dataType, url, contentType, data, (processData)屬性來做爲發起ajax的參數。其中也爲options設置了beforeSend, error方法做爲ajax的回調(success函數寫在其餘模塊中,詳情能夠看我以前的幾篇文章)。

其中還須要主要的有兩個參數emulateJSON & emulateHTTP。在文檔中的介紹很是詳細,我的以爲在大部分時候都不會用到。

4. 最後的話

終於把最後的第三篇文章寫出來了...花了很長時間...仍是以爲若是要真正徹底讀懂backbone源碼要多讀代碼(慚愧,自認爲還沒徹底達到),多查資料,多讀一些源碼解析。有關於routerhistory我以爲這篇文章仍是很棒的。這一個部分我的感受確實不是很好懂,可是能夠學習到不少有關路由的處理的相關知識,實際上是很是有益的~

backbone被稱爲框架的框架。這個框架的思想比起使用更有意義,畢竟如今有更多功能強大的框架。新東西要學,可是經典也是不該該被落下的。

若是這篇文章有什麼錯誤的地方請輕噴~互相學習!謝謝你們。

下面是所有的文章:
基於 Backbone + node 的我的簡歷生成器(我的學習總結)
Backbone源碼解讀(一)
Backbone源碼解讀(二)
Backbone源碼解讀(三)

相關文章
相關標籤/搜索