pjax는 pushState + ajax이며, 쉽게 사용할 수 있도록 jQuery 확장으로 캡슐화되어 있습니다. pjax는 주로 HTML 페이지가 URL을 부분적으로 새로 고치고 업데이트되지 않으며 앞뒤로 지원하지 않는 문제를 해결하여 사용자 경험을 향상시키는 데 사용됩니다.
pjax는 HTML5의 pushState() 및 replacementState()의 새로운 기능을 ajax와 결합하여 구현됩니다. pushState() 및 replacementState()는 기록 레코드를 추가 및 수정할 수 있는 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){})
.
이 메서드는 $.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回调 }) }
// 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)handSubmit()
// 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는 옵션 구성 및 이벤트 메커니즘을 지원합니다.
매개변수 이름 | 기본값 | Description |
---|---|---|
timeout | 650 | ajax 시간 초과(단위: ms), 기본값은 다음과 같습니다. 시간 초과 후 실행됨 페이지가 점프하고, 따라서 타임아웃이 너무 짧으면 안 되지만 일반적으로 설정할 필요는 없습니다 |
push | true | window.history.pushState를 사용하여 주소 표시줄 URL을 변경하세요(새로운 기록이 추가됩니다) |
replace | false | window.history.replaceState를 사용하여 주소 표시줄 URL을 변경하세요(기록이 추가되지 않음) |
maxCacheLength | 20 | 캐시된 기록 페이지 수(pjax는 다음의 내용을 캐시합니다. 새 페이지를 로드하기 전에 원본 페이지, 캐시 로드 후 스크립트가 다시 실행됩니다) |
version | 은 현재 페이지의 태그 내용인 pjax 버전을 반환하는 함수입니다. 페이지. response.setHeader("X-PJAX-Version", "")를 사용하여 현재 페이지와 다른 버전 번호를 설정하면 부분 새로 고침 | |
scrollTo | 0 | 세로 스크롤 대신 페이지가 강제로 이동할 수 있습니다. 페이지 로딩 후의 거리 (원래 페이지와 일치하면 전환 효과가 더 부드러워질 수 있음) |
type | "GET" | ajax 매개변수, http 요청 방법 |
dataType | "html" | ajax 매개변수, 응답 콘텐츠의 Content-Type |
container | 컨테이너를 찾는 데 사용되는 CSS 선택기, [container] 매개변수가 지정되지 않은 경우 사용됨 | |
url | link.href | 점프 연결의 경우 a 태그 |
fragment | 의 기본 href 속성은 응답 콘텐츠(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></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 원칙 및 사용법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!