【Jquery】Pjax的了解與應用
本來轉載自:www.ttkmwl.com --最有特色的程序代碼下載論壇-通天源碼論壇
什麼是pjax?
現在很多網站( facebook, twitter)都支持這樣的一種瀏覽方式, 當你點擊一個站內的鏈接的時候, 不是做頁麵跳轉, 而是隻是站內頁麵刷新。 這樣的用戶體驗, 比起整個頁麵都閃一下來說, 好很多。 其中有一個很重要的組成部分, 這些網站的ajax刷新是支持瀏覽器曆史的, 刷新頁麵的同時, 瀏覽器地址欄位上麵的地址也是會更改, 用瀏覽器的回退功能也能夠回退到上一個頁麵。 那麼如果我們想要實現這樣的功能, 我們如何做呢? 我發現pjax提供了一個腳本支持這樣的功能。 pjax項目地址在 https://github.com/defunkt/jquery-pjax 。
為什麼要用pjax?
pjax有好幾個好處:
用戶體驗提升。
頁麵跳轉的時候人眼需要對整個頁麵作重新識別, 刷新部分頁麵的時候, 隻需要重新識別其中一塊區域。 同時, 由於刷新部分頁麵的時候提供了一個loading的提示, 以及在刷新的時候舊頁麵還是顯示在瀏覽器中, 用戶能夠容忍更長的頁麵加載時間。
極大地減少帶寬消耗和服務器消耗。
由於隻是刷新部分頁麵, 大部分的請求(css/js)都不會重新獲取, 網站帶有用戶登錄信息的外框部分都不需要重新生成了。
如何使用pjax?
引入jquery和jquery.pjax.js
注冊事件
/**
* 方式一 按鈕父節點監聽事件
*
* @param selector 觸發點擊事件的按鈕
* @param container 展示刷新內容的容器,也就是會被替換的部分
* @param options 參數
*/
$(document).pjax(selector, [container], options);
// 方式二 直接對按鈕監聽,可以不用指定容器,使用按鈕的data-pjax屬性值查找容器
$("a[data-pjax]").pjax();
// 方式三 常規的點擊事件監聽方式
$(document).on('click', 'a', $.pjax.click);
$(document).on('click', 'a', function(event) {
var container = $(this).closest('[data-pjax-container]');
$.pjax.click(event, container);
});
// 下列是源碼中介紹的其他用法,
// 表單提交
$(document).on('submit', 'form', function(event) {
var container = $(this).closest('[data-pjax-container]');
$.pjax.submit(event, container);
});
// 加載內容到指定容器
$.pjax({ url: this.href, container: '#main' });
// 重新當前頁麵容器的內容
$.pjax.reload('#container');
具體文檔詳見:https://github.com/defunkt/jquery-pjax
pjax的原理
為了能夠處理問題, 我們需要能夠理解pjax的運作方式。 pjax的代碼隻有一個文件: https://github.com/defunkt/jquery-pjax/blob/master/jquery.pjax.js 如果有能力, 可以自己去看一遍。 我這裏解釋一下原理。 首先, 我們在html裏麵指定, 需要做pjax的鏈接內容是哪些, 以及點擊之後需要更新的部分(放在data-pjax屬性裏麵):
$('a[data-pjax]').pjax()
當加載了pjax腳本之後, 它會攔截這些鏈接的事件, 然後包裝成一個ajax請求, 發送給服務器。
$.fn.pjax = function( container, options ) {
return this.live('click.pjax', function(event){
handleClick(event, container, options)
})
}
function handleClick(event, container, options) {
$.pjax($.extend({}, defaults, options))
...
event.preventDefault()
}
var pjax = $.pjax = function( options ) {
...
pjax.xhr = $.ajax(options)
}
這個請求帶有X-PJAX的HEADER標識, 服務器在收到這樣的請求的時候, 就知道隻需要渲染部分頁麵返回就可以了。
xhr.setRequestHeader('X-PJAX', 'true')
xhr.setRequestHeader('X-PJAX-Container', context.selector)
pjax接受到返回的請求之後, 更新data-pjax指定的區域, 同時也會更新瀏覽器的地址。
options.success = function(data, status, xhr) {
var container = extractContainer(data, xhr, options)
...
if (container.title) document.title = container.title
context.html(container.contents)
}
為了能夠支持瀏覽器的後退, 利用到了history的api, 記錄下來對應的信息,
pjax.state = {
id: options.id || uniqueId(),
url: container.url,
container: context.selector,
fragment: options.fragment,
timeout: options.timeout
}
if (options.push || options.replace) {
window.history.replaceState(pjax.state, container.title, container.url)
}
當瀏覽器後退的時候, 攔截事件, 根據記錄的曆史信息, 產生一個新的ajax請求。
$(window).bind('popstate', function(event){
var state = event.state
if (state && state.container) {
var container = $(state.container)
if (container.length) {
...
var options = {
id: state.id,
url: state.url,
container: container,
push: false,
fragment: state.fragment,
timeout: state.timeout,
scrollTo: false
}
if (contents) {
// pjax event is deprecated
$(document).trigger('pjax', [null, options])
container.trigger('pjax:start', [null, options])
// end.pjax event is deprecated
container.trigger('start.pjax', [null, options])
container.html(contents)
pjax.state = state
container.trigger('pjax:end', [null, options])
// end.pjax event is deprecated
container.trigger('end.pjax', [null, options])
} else {
$.pjax(options)
}
...
}
}
}
為了支持fallback, 一個是在加載的時候判斷瀏覽器是否支持history push state API:
// Is pjax supported by this browser?
$.support.pjax =
window.history && window.history.pushState && window.history.replaceState
// pushState isn't reliable on iOS until 5.
&& !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/)
另一個是當發現請求一段時間沒有回複的時候(可以設置參數timeout), 直接做頁麵跳轉。
options.beforeSend = function(xhr, settings) {
if (settings.timeout > 0) {
timeoutTimer = setTimeout(function() {
if (fire('pjax:timeout', [xhr, options]))
xhr.abort('timeout')
}, settings.timeout)
// Clear timeout setting so jquerys internal timeout isn't invoked
settings.timeout = 0
pjax失效情況
會有一些情況導致pjax失效,下麵結合源碼分析下(省略部分無關代碼)
function handleClick(event, container, options) {
...
// 1. 點擊事件的事件源不是a標簽。使用a標簽可以做到對舊版本瀏覽器的兼容,所以不建議使用其他標簽注冊事件
if (link.tagName.toUpperCase() !== 'A')
throw "$.fn.pjax or $.pjax.click requires an anchor element"
// 2. 使用鼠標滾輪點擊(新標簽頁打開)
// 點擊超鏈接的同時按下Shift、Ctrl、Alt和Meta(在Windows鍵盤中是Windows鍵,在蘋果機中是Cmd鍵)
// 作用分別代表新窗口打開、新標簽打開(不切換標簽)、下載、新標簽打開(切換標簽)
if (event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
return
// 3. 跨域(網絡通訊協議,域名不一致)
if (location.protocol !== link.protocol || location.hostname !== link.hostname)
return
// 4. 當前頁麵的錨點定位
if (link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location))
return
// 5. 已經阻止元素發生默認的行為(url跳轉)
if (event.isDefaultPrevented())
return
...
var clickEvent = $.Event('pjax:click')
$(link).trigger(clickEvent, [opts])
// 6. pjax:click事件回調中已經阻止元素發生默認的行為(url跳轉)
if (!clickEvent.isDefaultPrevented()) {
pjax(opts)
event.preventDefault()// 阻止url跳轉
$(link).trigger('pjax:clicked', [opts])
}
}
除了上述情況之外,還有下列幾種情況:
ajax請求失敗,或者timeout後請求被中止
當前頁麵X-PJAX-Version和請求的新頁麵版本不一致
請求得到完整的頁麵(包含html標簽)卻沒設置fragment參數
Demo
index.PHP
<!DOCTYPE html>
<html>
<head>
<title>pjax</title>
<meta charset="utf-8">
</head>
<body>
<h1>My Site</h1>
<div>
Go to <a href="res1.php">第一頁</a>.<a href="res2.php">第二頁</a>
</div>
<div ></div>
</body>
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<script src="jquery.pjax.js"></script>
<script type="text/javascript">
$(document).pjax('a', '#container')
</script>
</html>
res1.php
<?php
echo "<div style='background:red;'>第一頁</div>";
res2.php
<?php
echo "<div style='background:red;'>第二頁</div>";
效果:
圖示
Demo2
index.html
<!DOCTYPE html>
<html>
<head>
<title>pjax</title>
<meta charset="utf-8">
</head>
<body>
<h1>My Site</h1>
<div>
<input type="button" value="GO">
</div>
<div ></div>
</body>
<script src="../jquery-2.1.4.min.js"></script>
<script src="../jquery.pjax.js"></script>
<script type="text/javascript">
$(function(){
$('#th').click(function(){
$.pjax({
url: './res3.php',
container: '#container'
});
});
});
</script>
</html>
re3.php
<?php
echo "<div style='background:red;'>第三頁</div>";
最後更新:2017-06-22 14:32:20