web發展經歷了一個漫長的週期,最開始不少人認爲Javascript這們語言是前端開發的累贅,是個雞肋,那個時候人們還享受着從一個a連接蹦到另外一個頁面的web神奇魔術。後來隨着JavaScript的不斷更新換代,他的功能不只僅是爲網頁添加一點特效了,語言自己的增強以及對DOM操做能力的提高讓他在前端大放光彩。尤爲是ajax的出現,讓JavaScript以及整個web的發展翻開了嶄新的一頁。php
利用ajax局部刷新頁面,相信不少人玩得至關熟練了。若是整個頁面的刷新都是使用ajax,咱們能夠稱之爲一個webapp,全部的邏輯都是在當頁處理,這種形式的頁面帶來的體驗是十分不錯的,減小了那些比較「冗餘」的頁面跳轉、新開頁面等。不過,webapp的代碼是十分很差維護的,頁面邏輯太多太深,出點小問題,整個頁面就會癱瘓,並且不方便定位bug,可維護性很低。html
ajax是一個很是好玩的小東西,不過用起來也會存在一些問題。前端
咱們能夠利用ajax進行無刷新改變文檔內容,可是沒辦法去修改URL,有童鞋要問,這裏爲何必定要修改URL呢?一個URL表明一個特定的網絡資源,ajax修改了頁面的內容,因此用不一樣的URL去標識他們,這個仍是挺有必要的。jquery
好比咱們設計了一個單詞查詢的頁面,比較合理的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的變化了。git
而這樣的方式對搜索引擎是十分不友好的,twitter和google約定使用hash bang (#!xxx),也就是hash後面的第一個字符爲感嘆號,這樣的網址他們是會爬取的,可是其餘搜索引擎不支持。PJAX能夠在改變頁面內容的同時也改變他的URL,下面來講說PJAX和他的應用。github
history API中有幾個新特性,分別是history.pushState和history.replaceState,咱們把pushState+AJAX進行封裝,合起來簡單點叫,就是PJAX~ 雖然說實現技術上沒什麼新東西,可是概念上仍是有所不一樣的。web
PJAX的基本思路是,用戶點擊一個連接,經過ajax更新頁面變化的部分,而後使用HTML5的pushState修改瀏覽器的URL地址,這樣有效地避免了整個頁面的從新加載。若是瀏覽器不支持history的兩個新API或者JS被禁用了,那這個連接就只能跳轉並從新刷新整個頁面了。和傳統的ajax設計稍微不一樣,ajax一般是從後臺獲取JSON數據,而後由前端解析渲染,而PJAX請求的是一個在服務器上生成好的HTML碎片,以下圖所示:ajax
客戶端向服務器發送一個普通的請求(1),其實也就是點擊了一個連接,服務器會相應這個請求(2),返回一個html文檔。客戶端向服務器發送一個有PJAX標誌的請求(3),此時服務器只返回一個html碎片(4)。可是這兩次請求都讓客戶端的URL變化了,但願上面的說明可讓你明白了PAJX和AJAX的區別了。chrome
先看一個小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">下一張>></a></span> <?php } else if ($num == 2) { ?> <div class="imgwrap"> <img src="./images/2.jpg" /> </div> <span><a href="num=1" class="previous"><<上一張</a> <a href="num=3" class="next">下一張>></a></span> <?php } else { ?> <div class="imgwrap"> <img src="./images/3.jpg" /> </div> <span><a href="num=2" class="previous"><<上一張</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加載,全部在須要這個東西的a標籤上加一個屬性,如data-pjax=true
,而後統一添加事件。