單頁面應用跳轉頁面方式

1、Pjaxjavascript

  像Github、百度、微博等這些大站,已經再也不使用普通的a標籤作跳轉了。他們大多使用Ajax請求替代了a標籤的默認跳轉,而後使用HTML5的新API修改了Url,你能夠在F12的Network面板裏發現這個祕密。
這項技術並無特別標準的學名,你們都稱呼爲Pjax,意爲PushState + Ajax。這並不徹底準確,由於還有Hash + Ajax等方法,但爲了方便,咱們下文仍是統稱爲Pjax。html

爲何要這麼作?

Pjax是一個優秀的解決方案,你有足夠多的理由來使用它:java

  • 能夠在頁面切換間平滑過渡,增長Loading動畫。
  • 能夠在各個頁面間傳遞數據,不依賴URL。
  • 能夠選擇性的保留狀態,如音樂網站,切換頁面時不會中止播放歌曲。
  • 全部的標籤均可以用來跳轉,不只僅是a標籤。
  • 避免了公共JS的反覆執行,如無需在各個頁面打開時都判斷是否登陸過等等。
  • 減小了請求體積,節省流量,加快頁面響應速度。
  • 平滑降級到低版本瀏覽器上,對SEO也不會有影響。

原理呢?

  Pjax的原理十分簡單。 1. 攔截a標籤的默認跳轉動做。 2. 使用Ajax請求新頁面。 3. 將返回的Html替換到頁面中。 4. 使用HTML5的History API或者Url的Hash修改Url。web

HTML5 History API

咱們來看看HTML5在History裏增長了什麼:api

history.pushState(state, title, url)
pushState方法會將當前的url添加到歷史記錄中,而後修改當前url爲新url。請注意,這個方法只會修改地址欄的Url顯示,但並不會發出任何請求。咱們正是基於此特性來實現Pjax。它有3個參數:瀏覽器

  • state: 能夠聽任意你想放的數據,它將附加到新url上,做爲該頁面信息的一個補充。
  • title: 顧名思義,就是document.title。不過這個參數目前並沒有做用,瀏覽器目前會選擇忽略它,傳null便可。
  • url: 新url,也就是你要顯示在地址欄上的url。

history.replaceState(state, title, url)
replaceState方法與pushState大同小異,區別只在於pushState會將當前url添加到歷史記錄,以後再修改url,而replaceState只是修改url,不添加歷史記錄。服務器

window.onpopstate 事件 通常來講,每當url變更時,popstate事件都會被觸發。但如果調用pushState來修改url,該事件則不會觸發,所以,咱們能夠把它用做瀏覽器的前進後退事件。該事件有一個參數,就是上文pushState方法的第一個參數state。閉包

一個實例:

這裏咱們以daipig爲例,打開daipig,地址欄是http://www.daipig.com 。接下來打開F12 Console,輸入:函數

history.pushState({ a: 1, b: 2 }, null, "http://www.daipig.com/abcdefg");

能夠發現,url已經變成咱們輸入的url了,但頁面並無刷新,也沒有發出任何請求。如今再輸入history.state,就能夠看到咱們剛剛傳過來的第一個參數state了。
這時點擊後退,url會回到www.daipig.com,一樣是沒有刷新。只不事後退的時候實際上是觸發了window.onpopstate事件的。測試

詳細文檔能夠查閱MDN: https://developer.mozilla.org/zh-CN/docs/DOM/Manipulating_the_browser_history

怎麼完整的實現Pjax?

Pjax的原理上文已經講了,並不複雜。我實現了一個比較粗糙的Pjax庫,已經能知足很多需求,地址在文末。
完整的代碼見Github,這裏咱們只談須要注意的一些地方。

匹配選擇器

要實現Pjax,不免就會有匹配選擇器的需求。你須要判斷當前點擊的元素,是否匹配指定選擇器。這裏我給出一個兼容至IE8的解決方法:

// 判斷element是否匹配選擇器selector
function matchSelector(element, selector) {
    var match = 
        document.documentElement.webkitMatchesSelector || 
        document.documentElement.mozMatchesSelector || 
        document.documentElement.msMatchesSelector ||
        // 兼容IE8及如下瀏覽器
        function(selector, element) {
            // 這是一個好方法,惋惜IE8連indexOf都不支持
            // return Array.prototype.indexOf.call(document.querySelectorAll(selector), this) !== -1;

            if (element.tagName === selector.toUpperCase()) return true;

            var elements = document.querySelectorAll(selector),
            length = elements.length;

            while (length--) {
            if (elements[length] === this) return true;
            }

            return false;
        };

    // 重寫函數自身,使用閉包keep住match函數,不用每次都判斷兼容
    matchSelector = function(element, selector) {
        return match.call(element, selector);
    };

    return matchSelector(element, selector);
}
// 驗證一下
matchSelector(document.getElementById("abc"), "#abc"); // true
matchSelector(document.querySelector("a"), "p"); // false

在現代瀏覽器上,優先使用原生的matchesSelector方法來判斷,在IE8及如下的瀏覽器裏,循環document.querySelector的結果集,依次對比。
這個方法利用了閉包,而後重寫自身,只有在第一次調用時須要判斷加哪一個前綴執行哪一個方法,其後都是調用了閉包的match函數。

不支持HTML5 PushState的瀏覽器怎麼辦?

IE6到IE9是不支持pushState的,要修改Url,只能利用Url的Hash,也便是#號。
你能夠隨意找個網站試一下,在url後面加上#號和任意內容,頁面並不會刷新。此時點擊後退也只會回到上一條#號,一樣不會刷新。
那麼咱們只需把pushState(新url)換成localtion.hash = 新url,把onpopstate事件換成onhashchange事件就能夠兼容IE了。 QQ音樂,網易雲音樂等就是使用這種方式。

 

2、只改變 URL Hash 的單頁面應用

仔細觀察能夠發現,如今的 Twitter,Google,Facebook 的 URL 地址充斥着 #號或者 #! 號。例如一個新版 Twitter 地址是這樣的

https://twitter.com/#!/chloerei

這個 #! 號有什麼意義呢?這個能夠看看阮一峯整理的這篇《URL的井號》。這裏假設你已經瞭解 # 號後面的改變不會致使頁面加載,怎麼利用這個特性達到 1.3 提出的目標。

第一步,有關 Ajax 調用的連接所有用 #path 做爲連接目標

例如,若是一個連接原本是

<a href="/topics/1">topic1</a>

就修改成

<a href="#/topics/1">topic1</a>

顯然,若是不作後續工做的話,這個連接點擊後頁面不會發生什麼變化,用戶也不會被帶到新地址。惟一的改變是 URL 的 # 號部分變成了 #topics/1

設置 onhashchange 事件

在 javascript api 中,窗口 window 對象的 hash 值(# 號後面部分)發生變化時,會調用 onhashchange 事件。給 onhashchange 掛上一個 function,就能夠在 hash 有改動的時候調用這個 function。

例如能夠在控制檯輸入這段 js 代碼測試

window.onhashchange = function(){alert(window.location.hash)}

實際中 function 裏面就是放置真正用來刷新頁面的代碼了。好比在 jQuery 裏用 $.get(location.path)。

效果

如今的 twitter,gooogle,facebook 都是使用這種方法刷新頁面,這對瀏覽器書籤、後退的支持也很好。

可是這有一些反作用。

一是由於頁面路徑被寫在了 Hash 裏面,而瀏覽器是不向服務器發送 Hash 部分的。因此打開這樣的 URL 須要兩個來回:一、打開空白的首頁 二、根據 Hash 用 Ajax 載入實際內容

二是把路徑寫在了 Hash 裏面破壞了 URL 原先的含義。從 URL 字面看

https://twitter.com/#!/chloerei

這個頁面表示的是 twitter.com 頁面上的 !/chloerie 錨點。但從實際內容上,這表示的是 chloerei 的我的頁面。因此有人稱這種站點爲「單頁面應用」。總的來講,這個方案對主流瀏覽器的支持程度很高,是目前的主流方案。

相關文章
相關標籤/搜索