標籤:

pjax 是如何工作的?

GitHub repo: https://github.com/defunkt/jquery-pjax

最近用了用 Google+, 發現它好像使用了 pjax,效果很贊。對於減少 HTTP 請求和重複請求很有幫助,同時也改善了用戶體驗。我知道它使用了 Ajax,但跟 Ajax 又有區別。就是在點擊導航鏈接時發生變化的是網頁局部,但瀏覽器的 url 也跟著變了(跟鏈接加#!方式不同)。在 GitHub 上也同樣使用類似東東。

我想問題它的工作原理,是如何做到不刷新頁面改變 url 的(非#號帶參數方式)?

還有,有沒有在不支持 HTML5 技術的瀏覽器上實現 pjax 的替代方案?


pjax是對ajax + pushState的封裝,讓你可以很方便的使用pushState技術。

同時支持了緩存和本地存儲,下次訪問的時候直接讀取本地數據,無需在次訪問。

並且展現方式支持動畫技術,可以使用系統自帶的動畫方式,也可以自定義動畫展現方式。

這是它的開源主頁 : https://github.com/welefen/pjax


其實pjax出現之前就有人用hash代替(就是#號),做全頁面ajax跳轉。

因為html5的新api:pushState和replaceState的出現,讓url脫離了#號。更重要的是支持了瀏覽器前進後退的事件觸發onpopstate。

pushState的功能具體來說就是修改url而頁面無跳轉,並且該url會被存放在歷史記錄中

當然為了滿足某些需要你不需要存放在歷史記錄中就需要使用replaceState

而瀏覽器上前進和後退都會觸發onpopstate能獲取你設置的State對象

拿welefen的pjax說下原理:

1.在用戶超鏈請求的時候攔截超鏈

2.攔截後同時發出兩種行為 ajax請求 以及 pushState

實際上他的設計是線性的,在超鏈被攔截後才會觸發。

我更傾向於觀察者模式,state被改變的時候觸發。

如果你真的想用在項目中我推薦你直接使用 balupton/history.js · GitHub

在不支持html5的瀏覽器上採用了hash作為代替並且監聽了statechange事件


AJAX + history.pushState,簡單來說就是瀏覽器的歷史記錄保存了對應的AJAX請求。

以 簡書 為例說明

1、進入頁面的時候是獲得整個HTML頁面 ,這沒什麼好說

2、點擊『最新動態』,從network就可以看到請求到的HTML片段

到這裡,其實只是AJAX請求HTML片段而已3、這時候,再點擊瀏覽器的後退,可以看到還是『最新文章』,但是network里沒有新的請求了。這是剛才的數據被緩存了,所以沒有新的請求。


http://www.barretlee.com/blog/2013/12/06/cb-history-api-in-html5/


主要看兩個方法:

一、pjax()

function pjax(options) {
options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
...
options.beforeSend = ...
options.complete = ...
options.error = ...
options.success = ...
...
var xhr = pjax.xhr = $.ajax(options)
return pjax.xhr
}

options里附加了$.ajaxSettings,因此裡面的beforeSend、complete、error、 success等就是ajax所對應的觸發事件。於是我們按觸發順序來看就一目了然了:

1. 初始化pjax.state

if (!pjax.state) {
pjax.state = {
id: uniqueId(),
url: window.location.href,
title: document.title,
container: options.container,
fragment: options.fragment,
timeout: options.timeout
}
window.history.replaceState(pjax.state, document.title)
}

2. 在發送前(beforeSend)中定義了一個專屬頭部,為了服務端能夠識別。

xhr.setRequestHeader("X-PJAX", "true")
xhr.setRequestHeader("X-PJAX-Container", options.container)

3. 發送出去後立即改變url:

if (xhr.readyState &> 0) {
if (options.push !options.replace) {
cachePush(pjax.state.id, [options.container, cloneContents(context)])
window.history.pushState(null, "", options.requestUrl)
}
...
}

4. 接受成功後(success)提取出內容並更新state

var container = extractContainer(data, xhr, options)
...
pjax.state = {
...
}

// 記錄當前state
if (options.push || options.replace) {
window.history.replaceState(pjax.state, container.title, container.url)
}

5. 最後渲染指定區域:

context.html(container.contents)

二、onPjaxPopstate()

在瀏覽器前進或後退時觸發該方法:

$(window).on("popstate.pjax", onPjaxPopstate)

先找到渲染的代碼:

...
var cache = cacheMapping[state.id] || []
var containerSelector = cache[0] || state.container
var container = $(containerSelector), contents = cache[1]
...
container.html(contents)

可以看出contents來自數組cacheMapping。那它是什麼時候存入頁面信息的?

回到一部分的步驟3有一個的cachePush的方法,找到該方法:

function cachePush(id, value) {
cacheMapping[id] = value
cacheBackStack.push(id)
...
}

對,就是它了。這意味每請求一個新的頁面,cacheMapping就會存入該頁面的內容。瀏覽器在返回或前進時,直接把內容從cacheMapping中取出來即可渲染。

參考:

1. https://developer.mozilla.org/zh-CN/docs/Web/API/History/pushState

2. https://developer.mozilla.org/zh-CN/docs/Web/API/Window/onpopstate

3. https://zhuanlan.zhihu.com/p/22412047

4. Pjax是什麼以及為什麼推薦大家用 - Sub的個人頁面


不知道能不能幫上你,給你個網址http://kayosite.com/html5-history-improve-ajax.html


我們公司也要求用這個技術,剛開始聽說,希望能有相關文檔看看


推薦閱讀:

Vuex與Redux的主要區別在哪裡,兩者各有什麼優缺點?
怎麼樣看待AngularJS從1升級到2的不兼容問題?
為什麼window.undefined要比undefined耗時?
阿里搶月餅的js代碼是如何實現的?
前端開發應更多地使用多層嵌套對象的結構,還是拍平數組進行引用的結構?

TAG:JavaScript | Ajax |