Maison  >  Article  >  interface Web  >  Principes et utilisation de Javascript PJAX

Principes et utilisation de Javascript PJAX

Guanhui
Guanhuiavant
2020-07-30 17:49:564533parcourir

Principes et utilisation de Javascript PJAX

pjax est pushState + ajax, qui est encapsulé dans une extension jQuery pour une utilisation facile. pjax est principalement utilisé pour résoudre le problème selon lequel la page HTML actualise partiellement l'URL et ne se met pas à jour et ne prend pas en charge les allers-retours, afin d'améliorer l'expérience utilisateur.

Principe de pjax

L'implémentation de pjax est implémentée en combinant les nouvelles fonctionnalités de pushState() et replaceState() de HTML5 avec ajax. pushState() et replaceState() sont utilisés pour faire fonctionner l'objet State, qui peut ajouter et modifier des enregistrements historiques, mettant ainsi à jour l'URL et fournissant des opérations avant et arrière. Ajax implémente le chargement asynchrone des données et l'actualisation partielle.

Organigramme du flux de travail

Principes et utilisation de Javascript PJAX

Analyse du code source

  • Jugement de support 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 est une méthode vide qui ne fait rien, c'est-à-dire function(){}. popstate.pjax est l'écriture de l'espace de noms des événements JS, et popstate est le type d'événement Chaque fois que l'historique activé change (le navigateur actionne les boutons avant et arrière, appelle la méthode back() ou go()), popstate sera. déclenché., mais l'appel de pushState() et replaceState() ne déclenchera pas l'événement popstate. .pjax est l'espace de noms de l'événement, ce qui facilite la dissociation de la réponse à l'événement de l'espace de noms spécifié. Il est souvent utilisé lors de la liaison de fonctions anonymes, telles que : this.on('click.pjax', selector, function(event){}).

  • fnPjax()

Cette méthode renvoie un objet jQuery, équivalent à $.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回调
    })
}
  • 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
}
  • Fonction de rappel

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 en utilisant

Après l'analyse ci-dessus, il peut être très simple Il est facile d'utiliser pjax maintenant.

Client

pjax prend en charge la configuration des options et le mécanisme d'événements.

  • configuration des 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 支持一些预定义的事件。

事件名 支持取消 参数 说明
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教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer