Javascript PJAX の原則と使用法

Guanhui
リリース: 2020-07-30 17:49:56
転載
4581 人が閲覧しました

Javascript PJAX の原則と使用法

pjax は PushState ajax で、使いやすいように jQuery 拡張機能にカプセル化されています。 pjax は主に、HTML ページが URL を部分的に更新して更新されない問題、および前後方向のサポートがない問題を解決して、ユーザー エクスペリエンスを向上させるために使用されます。

pjax の原則

pjax の実装は、HTML5 の PushState() および replaceState() の新機能と ajax を組み合わせることによって実現されます。 PushState() と replaceState() は、履歴レコードを追加および変更できる State オブジェクトの操作に使用され、これにより URL が更新され、順方向および逆方向の操作が提供されます。Ajax は、データの非同期ロードと部分的な更新を実装します。

作業フローチャート

Javascript PJAX の原則と使用法

ソースコード解析

  • pjaxが判断をサポート
(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)
ログイン後にコピー
  • enable()
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'、セレクター、関数(イベント){})

  • fnPjax()

このメソッドは、$.fn.pjax に相当する jQuery オブジェクトを返します。

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回调
    })
}
ログイン後にコピー
  • pjax()
// 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 use

上記の分析後、次のことができます。簡単に pjax を使うのは簡単になりました。

クライアント

pjax は、オプション構成とイベント メカニズムをサポートします。

    #オプション設定
パラメータ名デフォルト値説明#timeoutpushreplacemaxCacheLengthversion rollTo ##typedataType#containerurl を使用します。 fragment
  • pjax事件

为了方便扩展,pjax 支持一些预定义的事件。

650 ajax タイムアウト (単位はミリ秒)、デフォルトのページ ジャンプはタイムアウト後に実行されるため、タイムアウトは発生しません短すぎるはずですが、通常は設定する必要はありません
true window.history.pushState を使用してアドレス バーを変更しますURL (新しいものが追加されます履歴)
false window.history.replaceState を使用してアドレス バーの URL を変更します (履歴は追加されます)追加されません)
20 キャッシュされた履歴ページの数 (pjax は、新しいページをロードする前に元のページのコンテンツをキャッシュします)

は、pjax- を返す関数です。現在のページのバージョン、つまりページ内のタグのコンテンツ。現在のページとは異なるバージョン番号を設定するには、response.setHeader("X-PJAX-Version", "") を使用します。これにより、部分的に更新されるのではなく、ページが強制的にジャンプされる可能性があります。
0 ページが読み込まれた後の垂直スクロール距離 (元のページとの一貫性を維持すると、遷移効果がよりスムーズになります)
"GET" ajax、httpリクエストメソッドのパラメータ
"html" ajax パラメータ、応答コンテンツの Content-Type

コンテナの検索に使用される CSS セレクター。 [container] パラメーターが指定されていない場合は、
link.hrefジャンプされる接続、デフォルト タグ

の href 属性は、応答コンテンツ (CSS セレクター) を使用してページを設定します。サーバーはこのパラメーターを使用しません。ページ全体のリクエストを処理する場合は、このパラメーターを使用する必要があります。簡単に言うと、リクエストされたページをインターセプトすることを意味します
事件名 支持取消 参数 说明
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 :

  1. 引入jquery 和 jquery.pjax.js
  2. 注册事件

JS

<script></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

在 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,
        ));
    }
}
ログイン後にコピー

pjax失效情况

在以下 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 方案来实现

推荐教程:《PHP》《JS教程

以上がJavascript PJAX の原則と使用法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:jianshu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート