本篇文章給大家分享的是有關Javascript PJAX 原理是什么及如何使用,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
pjax 即 pushState + ajax,它被封裝成了一個 jQuery 擴展以方便使用。pjax 主要用來解決 HTML 頁面局部刷新 url 不更新和不支持后退和前進的問題,提升用戶體驗。
pjax 的實現是利用 HTML5 的 pushState() 和 replaceState() 新特性和 ajax 結合實現。pushState() 和 replaceState() 用來操作 State(狀態)對象,即可添加和修改歷史記錄,進而更新 url 和提供前進、后退操作 ajax 實現數據的異步加載進而局部刷新。

(function($){
$.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]\D|WebApps\/.+CFNetwork)/)
if ($.support.pjax){
enable() //啟用
} else {
disable() //禁用
}
})(jQuery)function enable() {
$.fn.pjax = fnPjax //注冊jQuery的pjax方法
$.pjax = pjax //注冊pjax對象
$.pjax.enable = $.noop
$.pjax.disable = disable
$.pjax.click = handleClick //注冊click回調
$.pjax.submit = handleSubmit //注冊submit回調
$.pjax.reload = pjaxReload //注冊reload回調
$.pjax.defaults = {} //設置默認值
$(window).on('popstate.pjax', onPjaxPopstate) //綁定popstate事件回調
}$.noop是一個空方法,不做任何事,即function(){}。popstate.pjax是 JS 事件的命名空間寫法,popstate是事件類型,每當激活的歷史記錄發生變化時(瀏覽器操作前進、后退按鈕、調用 back() 或者 go() 方法),都會觸發 popstate 事件,但調用 pushState()、replaceState() 不會觸發 popstate 事件。.pjax是該事件的命名空間,這樣方便解綁指定命名空間的事件響應,在綁定匿名函數時常使用,例如:this.on('click.pjax', selector, function(event){})。
該方法返回一個 jQuery 對象,等同于 $.fn.pjax。
return this.on('click.pjax', selector, function(event) {
//獲取pjax配置信息
options = optionsFor(container, options)
//自動綁定click事件響應
return this.on('click.pjax', selector, function(event) {
var opts = options
if (!opts.container) {
opts = $.extend({}, options)
//如果不配置container,則默認獲取data-pjax屬性值對應的
opts.container = $(this).attr('data-pjax')
}
handleClick(event, opts) //調用click回調
})
}// Use it just like $.ajax:
//
// var xhr = $.pjax({ url: this.href, container: '#main' })
// console.log( xhr.readyState )
//
// Returns whatever $.ajax returns.
function pjax(options) {
//獲取設置
options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
//判斷檢測
if (containerType !== 'string')
/**
* ajax響應回調注冊
*/
//beforeSend
options.beforeSend = function(xhr, settings) {
//設置pjax頭信息,供后端做兼容處理
xhr.setRequestHeader('X-PJAX', 'true')
xhr.setRequestHeader('X-PJAX-Container', options.container)
//設置超時
}
//complete
options.complete = function(xhr, textStatus) {
//綁定pjax:complete事件
fire('pjax:complete', [xhr, textStatus, options])
//綁定pjax:end事件
fire('pjax:end', [xhr, options])
}
//error
options.error = function(xhr, textStatus, errorThrown) {
//綁定pjax:error事件
fire('pjax:error', [xhr, textStatus, errorThrown, options])
}
//success,重點
options.success = function(data, status, xhr) {
//判斷檢測
if (currentVersion && latestVersion && currentVersion !== latestVersion)
... ...
window.history.replaceState(pjax.state, container.title, container.url)
//綁定pjax:beforeReplace事件
fire('pjax:beforeReplace', [container.contents, options], {
state: pjax.state,
previousState: previousState
})
//渲染頁面
context.html(container.contents)
//綁定pjax:success事件
fire('pjax:success', [data, status, xhr, options])
}
//初始化ajax
var xhr = pjax.xhr = $.ajax(options)
if (xhr.readyState > 0) {
//緩存頁面cache
cachePush(pjax.state.id, [options.container, cloneContents(context)])
//pushState
window.history.pushState(null, "", options.requestUrl)
//綁定pjax:start事件
fire('pjax:start', [xhr, options])
//綁定pjax:send事件
fire('pjax:send', [xhr, options])
}
//返回jQuery對象
return pjax.xhr
}1) handleClick()
// Examples
//
// $(document).on('click', 'a', $.pjax.click)
// // is the same as
// $(document).pjax('a')
//
// Returns nothing.
function handleClick(event, container, options) {
options = optionsFor(container, options)
//環境檢測
if (link.tagName.toUpperCase() !== 'A')
... ...
//綁定pjax:click事件
var clickEvent = $.Event('pjax:click')
$link.trigger(clickEvent, [opts])
//執行pjax
pjax(opts)
//成功則阻止默認行為
event.preventDefault()
//綁定pjax:clicked事件
$link.trigger('pjax:clicked', [opts])
}2)handleSubmit()
// Examples
//
// $(document).on('submit', 'form', function(event) {
// $.pjax.submit(event, '[data-pjax-container]')
// })
//
// Returns nothing.
function handleSubmit(event, container, options) {
options = optionsFor(container, options)
//環境檢測
if (form.tagName.toUpperCase() !== 'FORM')
... ...
//默認配置
var defaults = {
type: ($form.attr('method') || 'GET').toUpperCase(),
url: $form.attr('action'),
container: $form.attr('data-pjax'),
target: form
}
if (defaults.type !== 'GET' && window.FormData !== undefined) {
//POST時data域
defaults.data = new FormData(form)
}
//執行pjax
pjax($.extend({}, defaults, options))
//成功則阻止默認行為
event.preventDefault()
}3)pjaxReload()
// Reload current page with pjax.
function pjaxReload(container, options) {
var defaults = {
//當前url
url: window.location.href,
push: false,
replace: true,
scrollTo: false
}
//執行pjax
return pjax($.extend(defaults, optionsFor(container, options)))
}4)onPjaxPopstate()
// popstate handler takes care of the back and forward buttons
function onPjaxPopstate(event) {
//環境監測
if (state && state.container)
... ...
//獲取頁面cache
var cache = cacheMapping[state.id] || []
//綁定pjax:popstate事件
var popstateEvent = $.Event('pjax:popstate', {
state: state,
direction: direction
})
container.trigger(popstateEvent)
if (contents) {
//有頁面cache,直接渲染頁面
//綁定pjax:start事件
container.trigger('pjax:start', [null, options])
//綁定pjax:beforeReplace事件
var beforeReplaceEvent = $.Event('pjax:beforeReplace', {
state: state,
previousState: previousState
})
container.trigger(beforeReplaceEvent, [contents, options])
//渲染頁面
container.html(contents)
//綁定pjax:end事件
container.trigger('pjax:end', [null, options])
} else {
//無頁面cache,執行pjax
pjax(options)
}
}經過上述分析,就可以很容易使用 pjax 了。
pjax 支持 options 配置和事件機制。
| 參數名 | 默認值 | 說明 |
|---|---|---|
| timeout | 650 | ajax 超時時間(單位 ms),超時后會執行默認的頁面跳轉,所以超時時間不應過短,不過一般不需要設置 |
| push | true | 使用 window.history.pushState 改變地址欄 url(會添加新的歷史記錄) |
| replace | false | 使用 window.history.replaceState 改變地址欄 url(不會添加歷史記錄) |
| maxCacheLength | 20 | 緩存的歷史頁面個數(pjax 加載新頁面前會把原頁面的內容緩存起來,緩存加載后其中的腳本會再次執行) |
| version | 是一個函數,返回當前頁面的 pjax-version,即頁面中 標簽內容。使用 response.setHeader(“X-PJAX-Version”, “”) 設置與當前頁面不同的版本號,可強制頁面跳轉而不是局部刷新 | |
| scrollTo | 0 | 頁面加載后垂直滾動距離(與原頁面保持一致可使過度效果更平滑) |
| type | “GET” | ajax 的參數,http 請求方式 |
| dataType | “html” | ajax 的參數,響應內容的 Content-Type |
| container | 用于查找容器的 CSS 選擇器,[container] 參數沒有指定時使用 | |
| url | link.href | 要跳轉的連接,默認 a 標簽的 href 屬性 |
| fragment | 使用響應內容的指定部分(css 選擇器)填充頁面,服務端不進行處理導致全頁面請求的時候需要使用該參數,簡單的說就是對請求到的頁面做截取 |
為了方便擴展,pjax 支持一些預定義的事件。
| 事件名 | 支持取消 | 參數 | 說明 |
|---|---|---|---|
| pjax:click | ? | options | 點擊按鈕時觸發??烧{用 e.preventDefault() 取消 pjaxa |
| pjax:beforeSend | ? | xhr, options | ajax 執行 beforeSend 函數時觸發,可在回調函數中設置額外的請求頭參數??烧{用 e.preventDefault() 取消 pjax |
| pjax:start | xhr, options | pjax 開始(與服務器連接建立后觸發) | |
| pjax:send | xhr, options | pjax:start之后觸發 | |
| pjax:clicked | options | ajax 請求開始后觸發 | |
| pjax:beforeReplace | contents, options | ajax請求成功,內容替換渲染前觸發 | |
| pjax:success | data, status, xhr, options | 內容替換成功后觸發 | |
| pjax:timeout | ? | xhr, options | ajax 請求超時后觸發??烧{用 e.preventDefault() 繼續等待 ajax 請求結束 |
| pjax:error | ? | xhr, textStatus, error, options | ajax 請求失敗后觸發。默認失敗后會跳轉 url,如要阻止跳轉可調用 e.preventDefault() |
| pjax:complete | xhr, textStatus, options | ajax請求結束后觸發,不管成功還是失敗 | |
| pjax:end | xhr, options | pjax所有事件結束后觸發 | |
| pjax:popstate | forward / back(前進/后退) | ||
| pjax:start | null, options | pjax開始 | |
| pjax:beforeReplace | contents, options | 內容替換渲染前觸發,如果緩存了要導航頁面的內容則使用緩存,否則使用pjax加載 | |
| pjax:end | null, options | pjax結束 |
客戶端通過以下 2 個步驟就可以使用 pjax :
<script src="jquery.pjax.js"></script>
/**
* 方式1 監聽按鈕父節點事件
*/
$(document).pjax(selector, [container], options);
/**
* 方式2 直接監聽按鈕,可以不用指定容器,默認使用按鈕的data-pjax屬性值查找容器
*/
$("a[data-pjax]").pjax();
/**
* 方式3 主動綁定點擊事件監聽
*/
$(document).on('click', 'a', $.pjax.click);
$(document).on('click', 'a', function(event) {
//獲取container
var container = $(this).closest('[data-pjax-container]');
//click回調
$.pjax.click(event, container);
});
/**
* 方式4 主動綁定表單提交事件監聽
*/
$(document).on('submit', 'form', function(event) {
//獲取container
var container = $(this).closest('[data-pjax-container]');
//submit回調
$.pjax.submit(event, container);
});
/**
* 方式5 加載內容到指定容器
*/
$.pjax({url: this.href, container: '#main'});
/**
* 方式6 重新加載當前頁面容器的內容
*/
$.pjax.reload('#container');在 Yii 中,已經將 pjax 封裝成了 widgets,故在渲染時如下使用即可:
//view <?php Pjax::begin(); ?> ... ... <?php Pjax::end(); ?>
pjax 封裝成的 widgets 源碼文件widgets/Pjax.php ,事件注冊部分如下:
public function registerClientScript()
{
//a標簽的click
if ($this->linkSelector !== false) {
$linkSelector = Json::htmlEncode($this->linkSelector !== null ? $this->linkSelector : '#' . $id . ' a');
$js .= "jQuery(document).pjax($linkSelector, \"#$id\", $options);";
}
//form表單的submit
if ($this->formSelector !== false) {
$formSelector = Json::htmlEncode($this->formSelector !== null ? $this->formSelector : '#' . $id . ' form[data-pjax]');
$submitEvent = Json::htmlEncode($this->submitEvent);
$js .= "\njQuery(document).on($submitEvent, $formSelector, function (event) {jQuery.pjax.submit(event, '#$id', $options);});";
}
$view->registerJs($js);
}由于只是 HTML5 支持 pjax,所以后端需要做兼容處理。通過 X-PJAX 頭信息可得知客戶端是否支持 pjax,如果支持,則只返回局部頁面,否則 a 鏈接默認跳轉,返回整個頁面。
/**
* IndexController示例
*/
public function actionIndex() {
$dataProvider = new CActiveDataProvider('Article', array(
'criteria' => array('order' => 'create_time DESC')
));
//存在X-Pjax頭,支持pjax
if (Yii::$app->getRequest()->getHeaders()->get('X-Pjax')) {
//返回局部頁面
$this->renderPartial('index', array(
'dataProvider' => $dataProvider,
));
} else {
//返回整個頁面
$this->render('index', array(
'dataProvider' => $dataProvider,
));
}
}在以下 9 種情況時候 pjax 會失效,源碼部分如下:
//click回調
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
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. 已經阻止元素發生默認的行為
if (event.isDefaultPrevented())
return
...
var clickEvent = $.Event('pjax:click')
$(link).trigger(clickEvent, [opts])
// 6. pjax:click事件回調中已經阻止元素發生默認的行為
if (!clickEvent.isDefaultPrevented()) {
pjax(opts)
}
}
//pjax
function pjax(options) {
options.beforeSend = function(xhr, settings) {
//7. ajx超時
timeoutTimer = setTimeout(function() {
if (fire('pjax:timeout', [xhr, options]))
xhr.abort('timeout')
}, settings.timeout)
}
options.success = function(data, status, xhr) {
//8. 當前頁面和請求的新頁面版本不一致
if (currentVersion && latestVersion && currentVersion !== latestVersion) {
return
}
//9. ajax失敗
context.html(container.contents)
}除了使用 pjax 解決局部刷新并支持前進和后退問題外,也可以使用 browserstate/history.js + ajax 方案來實現
以上就是Javascript PJAX 原理是什么及如何使用,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。