閱讀920 返回首頁    go 阿裏雲 go 技術社區[雲棲]


【Jquery】Pjax的了解與應用

本來轉載自:www.ttkmwl.com  --最有特色的程序代碼下載論壇-通天源碼論壇

db94ca171ab2394b64937063bbe61997e11f795b

什麼是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

  上一篇:go  《軟件工藝師:專業、務實、自豪》一3.7.4 軟件工藝社團
  下一篇:go  《軟件工藝師:專業、務實、自豪》一3.7.3 軟件工藝師交換計劃