一.简介
pjax是一个jQuery插件,使用ajax和pushState技术提供快速的浏览体验与真正的永久链接、网页标题、以及浏览器的后退前进按钮操作。
pjax通过抓取HTML从您的服务器通过Ajax和更换容器页面上的HTML内容会与Ajax。然后更新无需重新加载你的网页的布局或任何资源使用pushstate浏览器的当前URL(JS,CSS),提供了一个快速的外观,全页面加载。但它确实就是Ajax和pushstate。
ajax缺点是破坏了浏览器的前进后退,因为ajax的请求不会留在历史记录中。pjax就不一样了,pjax被解释成ajax+pushState的封装,因为它把ajax的请求写入历史记录,并反映在地址栏,这样用户就能愉快地使用前进后退了。pjax有好几个实现方法,这里使用最常用的jQuery库,使用jquery.pjax.js。演示代码的服务器端使用PHP脚本语言。
Pjax用在那儿?就说百度云盘吧,这个大家肯定都用过。百度云盘PC端,在点击打开某个文件夹后会打开这个文件夹下的文件,其实显示文件的这个div就用到了pjax技术。地址栏变换,内容更换,但是却是一个ajax请求。等到后退的时候,不必重新请求上一层文件夹的内容,因为是存在在历史记录中的。而且,开发者还可以选择时候使用cache和storage缓存。
下载地址:
github:https://github.com/defunkt/jquery-pjax
CDN镜像:https://www.bootcdn.cn/jquery.pjax/
中文文档:http://bsify.admui.com/jquery-pjax/
超级简单的示例
百度网盘下载:https://pan.baidu.com/s/1_UMmGbCaBBQtrErxF8ApRg
注意:pjax必须使用http协议,即在服务器里使用^_^
实际的效果见: http://pjax.herokuapp.com 没有勾选 pjax 的时候点击链接是跳转的, 勾选了之后链接都是变成了 ajax 刷新(实际效果如下图的请求内容对比)。
不使用pjax
使用pjax
优点:
1)减轻服务端压力
按需请求,每次只需加载页面的部分内容,而不用重复加载一些公共的资源文件和不变的页面结构,大大减小了数据请求量,以减轻对服务器的带宽和性能压力,还大大提升了页面的加载速度。
2)优化页面跳转体验
常规页面跳转需要重新加载画面上的内容,会有明显的闪烁,而且往往和跳转前的页面没有连贯性,用户体验不是很好。如果再遇上页面比较庞大、网速又不是很好的情况,用户体验就更加雪上加霜了。使用pjax后,由于只刷新部分页面,切换效果更加流畅,而且可以定制过度动画,在等待页面加载的时候体验就比较舒服了。
缺点:
1)不支持一些低版本的浏览器(如IE系列)
pjax使用了pushState来改变地址栏的url,这是html5中history的新特性,在某些旧版浏览器中可能不支持。不过pjax会进行判断,功能不适用的时候会执行默认的页面跳转操作。
2)使服务端处理变得复杂
要做到普通请求返回完整页面,而pjax请求只返回部分页面,服务端就需要做一些特殊处理,当然这对于设计良好的后端框架来说,添加一些统一处理还是比较容易的,自然也没太大问题。另外,即使后台不做处理,设置pjax的fragment参数来达到同样的效果。
综合来看,pajx的优点很强势,缺点也几乎可以忽略,还是非常值得推荐的,尤其是类似博客这种大部分情况下只有主体内容变化的网站。关键它使用简单、学习成本小,即时全站只有极个别页面能用得到,尝试下没什么损失。pjax的github主页介绍的已经很详细了,想了解更多可以看下源码。
二.概述
pjax不是全自动的。您需要设置和指定一个包含在您的页面上的元素,当您浏览您的网站时将被替换。
可以参考下面的HTML代码结构和标签:
<!DOCTYPE html> <html> <head> <!-- styles, scripts, etc --> </head> <body> <h1>My Site</h1> <div class="container" id="pjax-container"> Go to <a href="/page/2">next page</a>. </div> </body> </html>
我们要pjax抓取URL/page/2然后替换#pjax-container不管它什么回来。没有风格或脚本将被加载,甚至<h1>可以保持不变 – 我们只是想改变#pjax-container元素。
我们会告诉pjax监听一个标签和使用#pjax-container作为目标容器:
$(document).pjax('a', '#pjax-container')
现在在pjax兼容浏览器,点击 “next page” 网页的内容容器#pjax-container中的内容将被/page/2内容替换。
魔法!差不多你还需要配置你的服务器来寻找pjax请求并发送回pjax具体内容。
pjax的ajax请求会发送一个X-PJAX文件头,因此,在这个例子中(在大多数情况下)我们希望将页面的内容返回,而不需要任何与该头文件的任何请求的布局。
这是它可能看起来像在Rails上:
def index if request.headers['X-PJAX'] render :layout => false
end
end
三.安装插件
通过bower安装
$ bower install jquery-pjax
或者,添加jquery-pjax到你的bower.json文件中。
"dependencies": { "jquery-pjax": "latest" }
通过npm安装
$ npm install jquery-pjax
通过js引入
pjax可以直接下载到你的应用程序的公共目录,请确保在此之前你已经加载jQuery。
curl -LO https://raw.github.com/defunkt/jquery-pjax/master/jquery.pjax.js
注意:不要盗链原始脚本的URL,Github不是一个CDN。
依赖关系
需要 jQuery 1.8.x 或者更高版本。
兼容性
pjax 只能工作在高级版本的浏览器中,点击这里查看浏览器对history.pushStateAPI 的兼容情况。 当API不支持pjax进入备用模式:$.fn.pjax的调用将无法运行,并且$.pjax将硬盘加载给定的网址。
用于调试目的,你可以故意甚至浏览器是否支持禁用pjaxpushState。只需要调用$.pjax.disable()。看看pjax实际上是支持pushState,调用$.support.pjax。
四.使用方法
1.引入 jquery 和 jquery.pjax.js
2.注册事件
/** * 方式一 按钮父节点监听事件 * * @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');
3.方法
1)$.fn.pjax
最简单常见的pjax使用方法如下:
$(document).pjax('a', '#pjax-container')
通过这种方式可以让页面中所有的链接都实现pjax加载,并指定#pjax-container作为容器元素。
如果您正在迁移已有网站,可能不希望在每个地方都使用pjax。那么您可以用data-pjax来注明这是一个pjax链接,然后使用a[data-pjax]来代替全局选择器a。或者,您也可以使用在<div data-pjax>容器中的<a data-pjax href="...">链接作为选择器。
$(document).pjax('[data-pjax] a, a[data-pjax]', '#pjax-container')
服务器端配置
理论上,您在服务器端可通过检查指定的X-PJAXHTTP头来识别pjax请求,并且只渲染指定的HTML内容,这也就意味着在浏览器端我们不用重新渲染整个页面,只替换指定容器元素(在我们的示例中是 #pjax-container)中的内容即可。下面的示例是在Ruby on Rails中的实现方法:
def index
if request.headers['X-PJAX']
render :layout => false
end
end
如果您想了解比上述方案更为自动化的方案,请查看 Turbolinks 。
看一下您喜欢的服务器端框架是否有对应的 pjax插件 。
也可以查看 RailsCasts #294: Playing with PJAX .
参数
$.fn.pjax 方法概述:
$(document).pjax(selector, [container], options)
selector:string类型,用于click 事件委托 的选择器。
container:string类型,用于标识唯一pjax容器的选择器。
options: object类型,包含下列选项。
pjax 可选参数:
参数名 | 默认值 | 说明 |
---|---|---|
timeout | 650 | ajax 超时时间(单位 ms ),超时后会执行默认的页面跳转,所以超时时间不应过短,不过一般不需要设置 |
push | true | 使用 window.history.pushState 改变地址栏 url (会添加新的历史记录) |
replace | false | 使用 window.history.replaceState 改变地址栏 url (不会添加历史记录) |
maxCacheLength | 20 | 缓存的历史页面个数(pjax 加载新页面前会把原页面的内容缓存起来,缓存加载后其中的脚本会再次执行) |
version | 是一个函数,返回当前页面的pjax-version,即页面中 <meta http-equiv="x-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属性 |
target | link | pjax事件参数e的 relatedTarget 属性,默认为点击的 a标签 |
fragment | 使用响应内容的指定部分(css选择器)填充页面,服务端不进行处理导致全页面请求的时候需要使用该参数,简单的说就是对请求到的页面做截取 |
您可以通过写全局更改默认设置$.pjax.defaults对象:
$.pjax.defaults.timeout = 1200
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 参数
2)$.pjax.click
这是由$.fn.pjax本身较低水平的功能。它可以让你获得更多的控制在pjax事件处理。
本例使用当前点击上下文来设置一个父级作为容器:
if ($.support.pjax) { $(document).on('click', 'a[data-pjax]', function(event) { var container = $(this).closest('[data-pjax-container]') $.pjax.click(event, { container: container }) }) }
注意:使用隐形的$.support.pjax保证,我们没有使用$.fn.pjax因此,我们应该避免结合此事件处理程序,除非浏览器实际上是要使用pjax。
3)$.pjax.submit
通过pjax提交表单。
$(document).on('submit', 'form[data-pjax]', function(event) { $.pjax.submit(event, '#pjax-container') })
4)$.pjax.reload
启动一个应用pjax机制服务器当前的URL请求和响应替换容器。不添加浏览器历史条目。
$.pjax.reload('#pjax-container', options)
5)$.pjax
手动调用pjax。主要用于当你想在一个处理程序,并不是来自于点击开始pjax请求。如果你可以得到一个点击event,容器$.pjax.click(event)代替。
function applyFilters() { var url = urlForFilters() $.pjax({url: url, container: '#pjax-container'}) }
4.事件
1. 点击链接后触发的一系列事件, 除了 pjax:click 和 pjax:clicked 的事件源是点击的按钮,其他事件的事件源都是要替换内容的容器。可以在 pjax:start 事件触发时开始过度动画,在 pjax:end 事件触发时结束过度动画。
事件名 | 支持取消 | 参数 | 说明 |
---|---|---|---|
pjax:click | ✔ | options | 点击按钮时触发。可调用 e.preventDefault(); 取消pjax |
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:beforeReplace 事件前 pjax 会调用 extractContainer 函数处理页面内容,即以 script[src] 的形式引入的 js 脚本不会被重复加载,有必要可以改下源码。
2. 浏览器前进/后退导航时触发的事件(暂时没做过多研究)
事件名 | 参数 | 说明 |
---|---|---|
pjax:popstate | 页面导航方向: 'forward'/'back'(前进/后退) | |
pjax:start | null, options | pjax 开始 |
pjax:beforeReplace | contents, options | 内容替换渲染前触发,如果缓存了要导航页面的内容则使用缓存,否则使用 pjax 加载 |
pjax:end | null, options | pjax 结束 |
pjax:send和pjax:complete如果你正在执行一个加载项,则是一个很好的事件使用。他们只会如果一个实际的XHR请求触发,如果内容是从缓存中加载:
$(document).on('pjax:send', function() { $('#loading').show() }) $(document).on('pjax:complete', function() { $('#loading').hide() })
取消的一个例子pjax:timeout事件是如果微调正在显示禁用回退超时行为:
$(document).on('pjax:timeout', function(event) { // Prevent default timeout redirection behavior event.preventDefault() })
五.高级配置
1.在新页面中重新初始化插件/工具
pjax的特点是它不会刷新页面即可获取并插入新内容。但是,如果其他jQuery插件(或库)为页面内容绑定了加载事件(如DOMContentLoaded),那么这些事件是无效的。 比较常用的一种做法是,在更新的页面内容范围内,重新初始化插件。
$(document).on('ready pjax:end', function(event) {
$(event.target).initializeMyPlugin()
})
该方法可以让$.fn.initializeMyPlugin()在页面普通加载和pjax加载时(点击链接或浏览器前进后退按钮之后)都能被调用。
2.强制重载的响应类型
默认情况下,如果pjax从服务器收到以下响应之一,则会强制重载页面:
---页面包含<html>标签,没有明确指定fragment选择器时。 pjax就会认为服务器端没有正确配置pjax响应。如果配置了fragment选项,pjax将根据该选择器提取页面内容。
---空白页面。pjax就会认为服务器端无法提供正确的pjax内容。
---HTTP状态码为4xx或5xx,表示某些服务器错误。
3.改变浏览器URL
如果服务器端需要改变浏览器地址栏中URL(如HTTP重定向),可以通过设置X-PJAX-URL头来实现:
def index
request.headers['X-PJAX-URL'] = "http://example.com/hello"
end
4.重载布局
静态资源或页面发生变化时,布局可被强制进行重载
首先,用一个自定义的meta标签在页面head中初始化layout版本。
<meta http-equiv="x-pjax-version" content="v123">
然后,在服务器端设置相同的X-PJAX-Version头。
if request.headers['X-PJAX']
response.headers['X-PJAX-Version'] = "v123"
end
部署后,版本不同时整个页面会强制重载,会重新发起请求来获取新的布局和相关资源。
六.jquery.pjax.js bug问题解决集锦
jquery.pjax 是一个很好的局部刷新插件,但实际应用过程是还是会有很多小问题,部分问题解决如下:
1.pjax 局部加载时候,IE 存在缓存问题,很容易理解,pjax是通过jquery的ajax加载局部内容的,默认cache=true,这会导致ie下get数据从缓存中获取,解决办法是设置pjax options的cache=false,这样请求会自动变成如下方式:
/XXXX?_pjax=%23pjax-container&_=1455092848927
2.pjax 与 jquery datatables一起使用,浏览器回退或前进的时候导致双表头,原因是浏览器回退或者前进的时候状态内容获取的是cacheMapping的数据,只需要将这个禁用即可解决datatables双表头问题
3.pjax 与 highcharts一起使用,到是error 16错误,重复定义Highcharts,原因是多次加载了js文件,原因很好理解,解决方式是初始化highchart时候,先delete (Highcharts);
4.pjax 加载带有<script type="text/javascript" src="xxx.js"></script> javascript inline的时候,存在无法加载的情况。pjax的机制是自动异步加载html内容,然后会自动将script[src]的内容加入到<head>标签最后,但经常会出现问题,这个机制是为了减少js的请求次数,不必每次都加载js,但产生bug的具体原因不明(有知道的请留言)。解决方案:禁止将script[src]的内容拷贝到<head>中,牺牲js缓存,也就是每次加载container都加载一次js,问题解决。
5.pjax 与 CodeMirror一起使用,导致CodeMirror无法正常加载的情况,解决方法设置一个延迟refresh()。
var pcEditor = CodeMirror.fromTextArea(document.getElementById('TplCntPc'), codeOpt); //bug,fixed by jackchain 20160604 setTimeout(function () { pcEditor.refresh(); }, 500);
原文地址:http://www.qingzz.cn/pjax_reference_translation
https://www.jianshu.com/p/557cad38e7dd
转载请注明: ITTXX.CN--分享互联网 » jQuery.pjax.js:使用AJAX和pushState无刷新加载网页详解
最后更新:2020-05-27 09:54:52