如何練就優(yōu)秀的項目體驗
創(chuàng)意、流程、執(zhí)行缺一不可
CREATIVITY, PROCESS, PERFORM INDISPENSABLE
spa單頁web應(yīng)用
2016-10-17
前言

不知你有沒有發(fā)現(xiàn),像Github、百度、微博等這些大站,已經(jīng)不再使用普通的a標(biāo)簽做跳轉(zhuǎn)了。他們大多使用Ajax請求替代了a標(biāo)簽的默認(rèn)跳轉(zhuǎn),然后使用HTML5的新API修改了Url,你可以在F12的Network面板里發(fā)現(xiàn)這個秘密。
這項技術(shù)并沒有特別標(biāo)準(zhǔn)的學(xué)名,大家都稱呼為Pjax,意為PushState + Ajax。這并不完全準(zhǔn)確,因為還有Hash + Ajax等方法,但為了方便,我們下文還是統(tǒng)稱為Pjax。


為什么要這么做?

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

  • 可以在頁面切換間平滑過渡,增加Loading動畫。
  • 可以在各個頁面間傳遞數(shù)據(jù),不依賴URL。
  • 可以選擇性的保留狀態(tài),如音樂網(wǎng)站,切換頁面時不會停止播放歌曲。
  • 所有的標(biāo)簽都可以用來跳轉(zhuǎn),不僅僅是a標(biāo)簽。
  • 避免了公共JS的反復(fù)執(zhí)行,如無需在各個頁面打開時都判斷是否登錄過等等。
  • 減少了請求體積,節(jié)省流量,加快頁面響應(yīng)速度。
  • 平滑降級到低版本瀏覽器上,對SEO也不會有影響。
原理呢?

Pjax的原理十分簡單。
1. 攔截a標(biāo)簽的默認(rèn)跳轉(zhuǎn)動作。
2. 使用Ajax請求新頁面。
3. 將返回的Html替換到頁面中。
4. 使用HTML5的History API或者Url的Hash修改Url。


HTML5 History API

我們來看看HTML5在History里增加了什么:


history.pushState(state, title, url)
pushState方法會將當(dāng)前的url添加到歷史記錄中,然后修改當(dāng)前url為新url。請注意,這個方法只會修改地址欄的Url顯示,但并不會發(fā)出任何請求。我們正是基于此特性來實現(xiàn)Pjax。它有3個參數(shù):

  • state: 可以放任意你想放的數(shù)據(jù),它將附加到新url上,作為該頁面信息的一個補充。
  • title: 顧名思義,就是document.title。不過這個參數(shù)目前并無作用,瀏覽器目前會選擇忽略它。
  • url: 新url,也就是你要顯示在地址欄上的url。

history.replaceState(state, title, url)
replaceState方法與pushState大同小異,區(qū)別只在于pushState會將當(dāng)前url添加到歷史記錄,之后再修改url,而replaceState只是修改url,不添加歷史記錄。


window.onpopstate 事件
一般來說,每當(dāng)url變動時,popstate事件都會被觸發(fā)。但若是調(diào)用pushState來修改url,該事件則不會觸發(fā),因此,我們可以把它用作瀏覽器的前進(jìn)后退事件。該事件有一個參數(shù),就是上文pushState方法的第一個參數(shù)state。


一個實例:

這里我們以daipig為例,打開daipig,地址欄是http://ydwgb.cn 。接下來打開F12 Console,輸入:

history.pushState({ a: 1, b: 2 }, null, "http://ydwgb.cn/abcdefg");

可以發(fā)現(xiàn),url已經(jīng)變成我們輸入的url了,但頁面并沒有刷新,也沒有發(fā)出任何請求。現(xiàn)在再輸入history.state,就可以看到我們剛剛傳過來的第一個參數(shù)state了。
這時點擊后退,url會回到ydwgb.cn,同樣是沒有刷新。只不過后退的時候其實是觸發(fā)了window.onpopstate事件的。

詳細(xì)文檔可以查閱MDN: https://developer.mozilla.org/zh-CN/docs/DOM/Manipulating_the_browser_...


怎么完整的實現(xiàn)Pjax?

Pjax的原理上文已經(jīng)講了,并不復(fù)雜。我實現(xiàn)了一個比較粗糙的Pjax庫,已經(jīng)能滿足不少需求,如果你有興趣,可以上Github幫忙完善一下代碼。地址是:https://github.com/Coffcer/coffce-pjax 。
完整的代碼見Github,這里我們只談需要注意的一些地方。

匹配選擇器

要實現(xiàn)Pjax,難免就會有匹配選擇器的需求。你需要判斷當(dāng)前點擊的元素,是否匹配指定選擇器。這里我給出一個兼容至IE8的解決方法:

// 判斷element是否匹配選擇器
selectorfunction 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;
        };

// 重寫函數(shù)自身,使用閉包keep住match函數(shù),不用每次都判斷兼容
    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


在現(xiàn)代瀏覽器上,優(yōu)先使用原生的matchesSelector方法來判斷,在IE8及以下的瀏覽器里,循環(huán)document.querySelector的結(jié)果集,依次對比。
這個方法利用了閉包,然后重寫自身,只有在第一次調(diào)用時需要判斷加哪個前綴執(zhí)行哪個方法,其后都是調(diào)用了閉包的match函數(shù)。


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

IE6到IE9是不支持pushState的,要修改Url,只能利用Url的Hash,也即是#號。
你可以隨意找個網(wǎng)站試一下,在url后面加上#號和任意內(nèi)容,頁面并不會刷新。此時點擊后退也只會回到上一條#號,同樣不會刷新。
那么我們只需把pushState(新url)換成localtion.hash = 新url,把onpopstate事件換成onhashchange事件就可以兼容IE了。
QQ音樂,網(wǎng)易云音樂等就是使用這種方式。

現(xiàn)成的庫

我簡單實現(xiàn)了一個比較粗糙的Pjax庫,地址是:https://github.com/Coffcer/coffce-pjax ,歡迎PR和Star。作者:coffee

CONTACT US
  • 服務(wù)熱線:18664767192
  • 廣州技術(shù)總部:廣州市天河區(qū)天河北路179號尚層國際21層
  • 郵箱:admin@bangju.com
掃一掃加客服微信