PJAX的實現與應用

一、前言

web發展經歷了一個漫長的週期,最開始很多人認爲Javascript這們語言是前端開發的累贅,是個雞肋,那個時候人們還享受着從一個a鏈接蹦 到另一個頁面的web神奇魔術。後來隨着JavaScript的不斷更新換代,他的功能不僅僅是爲網頁添加一點特效了,語言本身的加強以及對DOM操作能 力的提升讓他在前端大放光彩。尤其是ajax的出現,讓JavaScript以及整個web的發展翻開了嶄新的一頁。

利用ajax局部刷新頁面,相信很多人玩得相當熟練了。如果整個頁面的刷新都是使用ajax,我們可以稱之爲一個webapp,所有的邏輯都是在當 頁處理,這種形式的頁面帶來的體驗是十分不錯的,減少了那些比較“冗餘”的頁面跳轉、新開頁面等。不過,webapp的代碼是十分不好維護的,頁面邏輯太 多太深,出點小問題,整個頁面就會癱瘓,而且不方便定位bug,可維護性很低。

二、PJAX的實現與應用

1.場景再現-ajax弊端

ajax是一個非常好玩的小東西,不過用起來也會存在一些問題。

我們可以利用ajax進行無刷新改變文檔內容,但是沒辦法去修改URL,有童鞋要問,這裏爲什麼一定要修改URL呢?一個URL代表一個特定的網絡資源,ajax修改了頁面的內容,所以用不同的URL去標識他們,這個還是挺有必要的。

比如我們設計了一個單詞查詢的頁面,比較合理的UR應該是http://example.com/word,不同的word對應不同的內容,但是如 果整個頁面都是ajax實現,我們就沒法去修改/word了,當然我們可以使用hash如http://example.com#word,但這樣就不能 很好的處理瀏覽器的前進和後退問題。如:在頁面中查詢了單詞A的翻譯,接着又查詢了單詞B,這個時候瀏覽器的瀏覽歷史會生成 http://example.com#A和http://example.com#B兩個記錄,可是當我們從B轉回A的時候,AJAX的效果還停留在B 的狀態(頁面顯示的還是單詞B的翻譯)。部分瀏覽器對此問題引入了onhashchange的接口,只要URL的hash值發生變化,我們的程序就可以監 聽並做出相應。不過對於那些木有這個接口的瀏覽器,就得定時去判斷hash的變化了。

而這樣的方式對搜索引擎是十分不友好的,twitter和google約定使用hash bang (#!xxx),也就是hash後面的第一個字符爲感嘆號,這樣的網址他們是會爬取的,但是其他搜索引擎不支持。PJAX可以在改變頁面內容的同時也改變 他的URL,下面來說說PJAX和他的應用。

2.什麼是PJAX

history API中有幾個新特性,分別是history.pushStatehistory.replaceState,我們把pushState+AJAX進行封裝,合起來簡單點叫,就是PJAX~ 雖說實現技術上沒什麼新東西,但是概念上還是有所不同的。

PJAX的基本思路是,用戶點擊一個鏈接,通過ajax更新頁面變化的部分,然後使用HTML5pushState修改瀏覽器的URL地址,這樣 有效地避免了整個頁面的重新加載。如果瀏覽器不支持history的兩個新API或者JS被禁用了,那這個鏈接就只能跳轉並重新刷新整個頁面了。和傳統的 ajax設計稍微不同,ajax通常是從後臺獲取JSON數據,然後由前端解析渲染,而PJAX請求的是一個在服務器上生成好的HTML碎片。

客戶端向服務器發送一個普通的請求(1),其實也就是點擊了一個鏈接,服務器會相應這個請求(2),返回一個html文檔。客戶端向服務器發送一個 有PJAX標誌的請求(3),此時服務器只返回一個html碎片(4)。但是這兩次請求都讓客戶端的URL變化了,希望上面的說明可以讓你明白了PAJX 和AJAX的區別了。

3.PJAX的實現

先看一個小DEMO吧,這個DEMO也寫了我半個多小時,看之前先說明一下,打開你的現代瀏覽器(chrome,Firefox,opera,IE9+等),進入gallery頁面,查看圖片的時候注意觀察瀏覽器的title和url變化,點擊前進後退按鈕也注意查看其變化。我已經在瀏覽歷史管理中push了三條歷史記錄。

DEMO地址:http://qianduannotes.duapp.com/demo/PJAX/index.html

如果你還沒有理解上面說的PJAX和AJAX的區別,看完這個demo,你應該有所領悟吧!在URL變化之後,頁面並沒有刷新,而是繼續完成自己的動畫(demo中爲fadeOut)。

在HTML4,Histroy對象有下面屬性方法:

length:歷史堆棧中的記錄數。
back():返回上一頁。
forward():前進到下一頁。
go([delta]):delta是個數字,如果不寫或爲0,則刷新本頁;如果爲正數,則前進到相應數目的頁面;若爲負數,則後退到相應數目的頁面。
在HTML5中,新增了兩個方法:

pushState(data, title [, url]):往歷史堆棧的頂部添加一條記錄。data爲一個對象 或null,它會在觸發window的popstate事件(window.onpopstate)時,作爲參數的state屬性傳遞過去;title爲 頁面的標題,但當前所有瀏覽器都忽略這個參數;url爲頁面的URL,不寫則爲當前頁。
replaceState(data, title [, url]):更改當前頁面的歷史記錄。參數同上。這種更改並不會去訪問該URL。
當點擊“上一張”、“下一張”這兩個鏈接的時候,首先通過pushState修改URL以及修改document.title,那這個時候你就可以 當做文檔已經進入了另外一個鏈接了,然後該幹什麼幹什麼。demo中是讓圖片fadeOut,fadeOut完了之後讓瀏覽器去加載資源,這個步驟就是正 常的AJAX操作啦,沒有什麼特殊之處了~

因爲只准備了三張圖片,所有後臺寫的也比較簡單:

<?php
    error_reporting(false);
    
    $num = $_GET['num'];
    
    if(array_key_exists('HTTP_X_PJAX', $_SERVER) && $_SERVER['HTTP_X_PJAX'] === 'true'){
        if($num == 1) {
    ?>
            <div class="imgwrap">
                <img src="./images/1.jpg" />
            </div>
            <span><a href="num=2" class="next">下一張&gt;&gt;</a></span>
    <?php
        } else if ($num == 2) {
    ?>
            <div class="imgwrap">
                <img src="./images/2.jpg" />
            </div>
            <span><a href="num=1" class="previous">&lt;&lt;上一張</a>
            <a href="num=3" class="next">下一張&gt;&gt;</a></span>
    <?php
        } else {
    ?>
            <div class="imgwrap">
                <img src="./images/3.jpg" />
            </div>
            <span><a href="num=2" class="previous">&lt;&lt;上一張</a></span>
    <?php
        }
    }
?>

上面那張圖中,我們看到了,並不是每個連接都使用PJAX來加載,如果有X_PJAX標識,我們纔會添加相應的處理。js中稍加註意可以看到:

$.ajax({
    "url": "./interface.php",
    "data": {
        "num": num
    },
    "dataType": "html",
    "headers": {
        "X_PJAX": true
    }
});

請求中:

Accept:text/html, */*; q=0.01
Accept-Encoding:gzip,deflate,sdch
Connection:keep-alive
Host:qianduannotes.duapp.com
User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
X-Requested-With:XMLHttpRequest
X_PJAX:true

我在請求的header中加了一個X_PJAX的頭,而後臺在處理的時候也做了判斷:

function is_pjax(){
    return array_key_exists('HTTP_X_PJAX', $_SERVER) 
            && $_SERVER['HTTP_X_PJAX'] === 'true';
}

並不是一定要求在header頭部中加入X_PJAX的信息,你也可以在url中加入相關的參數,比如:http://example.com?pjax=1,或者其他方式,只要前後端達到一個共識就行。

三、開源的PJAX庫

已經有人對這個東西做了封裝,我就不重複造輪子了。

welefen封裝的庫,對jquery、qwrap和kissy都做了封裝,github地址
Yahoo團隊 PJAX地址
並不是頁面中所有的鏈接都需要使用PJAX加載,所有在需要這個東西的a標籤上加一個屬性,如data-pjax=true,然後統一添加事件。

四、注意事項

如果瀏覽器不支持pushState接口函數,那就只能退化爲ajax或者使用hash bang了~
本地環境下使用的話,瀏覽器會報錯:``Uncaught SecurityError: A history state object with URL file:///E:/baidu_app/demo/PJAX/pic-2’ cannot be created in a document with origin ‘null’. ,所以如果你要測試的話,請把代碼丟到服務器上! 爲了得到更好的體驗,PJAX經常配合localStorage`來使用,把請求到的內容緩存到本地,再一次請求的時候先從本地查看。如果你的內容是動態變化的,緩存的時候加一個緩存時間,方便更新緩存。
還有一個容易忽略的東西是統計,使用PJAX只會局部刷新頁面,如果忽略了對統計函數的更新,那就會讓你失去很多數據。

五、參考資料

http://www.welefen.com/pjax-for-ajax-and-pushstate.html welefen
http://ntotten.com/2012/04/09/building-super-fast-web-apps-with-pjax/ Nathan Totten
http://yuilibrary.com/yui/docs/pjax/ YUI Pjax

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章