pjax = pushState + ajax
pjax它使用ajax和pushState两个技术改善用户的网页浏览体验。具体来说,当用户使用a标签切换页面时,可以实现局部刷新的技术。
pjax主要做两方面的事儿:
- 用户点击链接发送
ajax
请求,服务器得到请求返回需要填充的HTML片段,客户端得到HTML片段然后插入更新区域 - 页面填充完毕后,使用
pushState
更新当前的URL
这个过程能实现页面局部刷新,比传统的页面切换刷新的体验好一些,因为:
- 只下载需要的HTML页面片段,没有JS、CSS解析
- 如果服务端配置了正确的pjax请求,则只返回要更新的HTML片段,客户端只更新必要的内容,避免了页面重新渲染的过程。
如何使用
1. 客户端
客户端设置分两步:
- 下载插件,包括jquery1.8+,或者npm安装,这部分参考文档,不赘述。
- 初始化pjax插件,并有条件的拦截
a
标签跳转。
初始化$.fn.pjax
下面代码表示:当selector
被点击时,执行ajax请求,并将返回的HTML字符串填充在container
标记的位置。
1 |
$(document).pjax(selector, [container], options) |
参数说明
- selector:click事件的选择器
- container:pjax容器id
- options :配置参数
pjax options
key | default | description |
---|---|---|
timeout |
650 | ajax请求如果超时将触发强制刷新 |
push |
true | 使用 [pushState][] 在浏览器中添加导航记录 |
replace |
false | 是否使用replace方式改变URL |
maxCacheLength |
20 | 返回的HTML片段字符串最大缓存数 |
version |
当前pjax版本 | |
scrollTo |
0 | 当页面导航切换时滚动到的位置. 如果想页面切换不做滚动重置处理,请传入false . |
type |
"GET" |
使用ajax的模板请求方法,参考 $.ajax |
dataType |
"html" |
模板请求时的type,参考 $.ajax |
container |
内容替换的CSS选择器 | |
url |
link.href | 用于ajax请求的url,可以是字符串或者返回字符串的函数 |
target |
link | eventually the relatedTarget value for pjax events |
fragment |
从服务端返回的HTML字符串中子内容所在的CSS选择器,用于当服务端返回了整个HTML文档,但要求pjax局部刷新时使用。 |
可以使用下面的方式动态设置options:
1 |
$.pjax.defaults.timeout = 1200 |
初始化一般的做法是做好HTML结构,有条件的触发pjax跳转请求:
1 2 3 4 5 6 7 |
<div data-pjax> <a data-pjax href="/to/somewhere">ToSomewhere1</a> <a data-pjax href="/to/somewhere">ToSomewhere2/a> </div> <section id="pjax-container"> <!-- 在这里更新返回的HTML字符串 --> </section> |
1 |
$(document).pjax('[data-pjax] a, a[data-pjax]', '#pjax-container') |
2. 服务端
服务端也比较简单,监听HTTP的header中有X-PJAX
的ajax请求,如果有则返回HTML片段,而不是整个HTML。
API介绍
这部分用于更细粒度的控制。
$.pjax.click
示例:
1 2 3 4 5 6 7 8 |
// 确定能使用pjax时 if ($.support.pjax) { $(document).on('click', 'a[data-pjax]', function(event) { var container = $(this).closest('[data-pjax-container]') var containerSelector = '#' + container.id $.pjax.click(event, {container: containerSelector}) }) } |
$.pjax.submit
用pjax提交表单
1 2 3 |
$(document).on('submit', 'form[data-pjax]', function(event) { $.pjax.submit(event, '#pjax-container') }) |
$.pjax.reload
对当前URL使用pjax的方式重新获取HTML代码片段,并且在指定容器替换,这个过程不添加新的历史记录。(子片段重刷新)
1 |
$.pjax.reload('#pjax-container', options) |
$.pjax
不是通过click
触发pjax的时候使用。比如某些操作后自动触发pjax的过程。如果能获取到click
的event
事件时,建议使用$.pjax-click(event)
替换。
1 2 3 4 |
function applyFilters() { var url = urlForFilters() $.pjax({url: url, container: '#pjax-container'}) } |
pjax生命周期
pjax生命周期简单的说:
生命周期和Loading组件使用密切:
1 2 3 4 5 6 |
$(document).on('pjax:send', function() { $('#loading').show() }) $(document).on('pjax:complete', function() { $('#loading').hide() }) |
高级技巧
子页面加载完毕初始化其中的插件/组件
pjax只是请求HTML片段之后插入指定位置,因此片段内的JS插件/组件初始化需要在pjax:end
事件后执行。
1 2 3 |
$(document).on('ready pjax:end', function(event) { $(event.target).initializeMyPlugin() }) |
这段代码会在document ready或者container ready后执行initializeMyPlugin
初始化方法(包括前进后退)。
强制reload
当使用pjax导致整个页面被强制刷时,可能的原因是:
- 当返回的HTML片段包含
<html>
标签且fragment
选择器没有指定时。如果指定了fragment
选择器,pjax将从HTML文档中提取需要局部刷新的子片段。 - 服务端返回的内容为空时。
- HTTP响应的code是 4xx 或者 5xx。
浏览器重定向
在响应头中设置X-PJAX-URL
,例如:
1 |
request.headers['X-PJAX-URL'] = "https://www.jonhuu.com/hello" |
Layout重新加载
当客户端页面的pjax版本和服务器返回的pjax版本不一致时,页面会重新刷新。
客户端页面的pjax版本:
1 2 |
<span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"x-pjax-version"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"v123"</span>></span> |
如果服务器修改了版本则重新刷新:
1 2 |
response.headers[<span class="hljs-string">'X-PJAX-Version'</span>] = <span class="hljs-string">"xxxx修改版本名称xxxx"</span> |
使用建议
pjax需要服务端密切配合,如果服务端没设置好(比如nginx配置),要不就是请求只返回HTML片段,要不每次页面切换都是重新加载页面。
像自主能控制nodejs层的话,可以有效的管理,以下代码是node中用koa框架,主要关注x-pjax那块即可,
利用PJAX就局部刷新页面,不会重复加载页面资源等。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
async (ctx, next) => { const html = await ctx.render('books/pages/index',{ data: result.query }); // pjax会给请求头加入信息--直接判断是否是pjax的 if(ctx.request.header['x-pjax']){ const $ = cheerio.load(html); // 引入加载好的整体页面,cheerio可以获取整体页面,然后JQ控制操作 // 只获取一小段html代码渲染 ctx.body = $('#js-books-data').html(); // 获取到指定id下面的所有html代码 }else{ ctx.body = html; // 默认没有pjax渲染所有 } }; |