欢迎您的光临,本博所发布之文章皆为作者亲测通过,如有错误,欢迎通过各种方式指正。

文摘  jQuery.pjax.js:使用AJAX和pushState无刷新加载网页详解

JQuery 本站 2507 1评论

1836534-eaf59219cb0c2b53.webp.jpg

一.简介


pjax是一个jQuery插件,使用ajax和pushState技术提供快速的浏览体验与真正的永久链接、网页标题、以及浏览器的后退前进按钮操作。

pjax通过抓取HTML从您的服务器通过Ajax和更换容器页面上的HTML内容会与Ajax。然后更新无需重新加载你的网页的布局或任何资源使用pushstate浏览器的当前URL(JS,CSS),提供了一个快速的外观,全页面加载。但它确实就是Ajax和pushstate。

点击这里查看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 刷新(实际效果如下图的请求内容对比)。

1836534-6c68deeadf0b92a6.webp.jpg不使用pjax

1836534-05ebad6a69e1acfe.webp.jpg使用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 可选参数:

参数名默认值说明
timeout650ajax 超时时间(单位 ms),超时后会执行默认的页面跳转,所以超时时间不应过短,不过一般不需要设置
pushtrue使用 window.history.pushState 改变地址栏 url(会添加新的历史记录)
replacefalse使用 window.history.replaceState 改变地址栏 url(不会添加历史记录)
maxCacheLength20缓存的历史页面个数(pjax 加载新页面前会把原页面的内容缓存起来,缓存加载后其中的脚本会再次执行)
version
是一个函数,返回当前页面的pjax-version,即页面中 <meta http-equiv="x-pjax-version"> 标签内容。使用 response.setHeader("X-PJAX-Version", "") 设置与当前页面不同的版本号,可强制页面跳转而不是局部刷新。
scrollTo0页面加载后垂直滚动距离(与原页面保持一致可使过度效果更平滑)
type"GET"ajax 的参数,http 请求方式
dataType"html"ajax的参数,响应内容的 Content-Type
container
用于查找容器的CSS选择器,[container]参数没有指定时使用
urllink.href要跳转的连接,默认a标签的href属性
targetlinkpjax事件参数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:clickoptions点击按钮时触发。可调用 e.preventDefault(); 取消pjax
pjax:beforeSendxhr, optionsajax 执行 beforeSend 函数时触发,可在回调函数中设置额外的请求头参数。可调用 e.preventDefault(); 取消 pjax
pjax:start
xhr, optionspjax开始(与服务器连接建立后触发)
pjax:send
xhr, optionspjax:start 之后触发
pjax:clicked
optionsajax 请求开始后触发
pjax:beforeReplace
contents, optionsajax 请求成功,内容替换渲染前触发
pjax:success
data, status, xhr, options内容替换成功后触发
pjax:timeoutxhr, optionsajax请求超时后触发。可调用 e.preventDefault(); 继续等待 ajax 请求结束
pjax:errorxhr, textStatus, error, optionsajax 请求失败后触发。默认失败后会跳转url,如要阻止跳转可调用 e.preventDefault();
pjax:complete
xhr, textStatus, optionsajax 请求结束后触发,不管成功还是失败
pjax:end
xhr, optionspjax 所有事件结束后触发

注意:pjax:beforeReplace 事件前 pjax 会调用 extractContainer 函数处理页面内容,即以 script[src] 的形式引入的 js 脚本不会被重复加载,有必要可以改下源码。


2. 浏览器前进/后退导航时触发的事件(暂时没做过多研究)

事件名参数说明
pjax:popstate
页面导航方向: 'forward'/'back'(前进/后退)
pjax:startnull, optionspjax 开始
pjax:beforeReplacecontents, options内容替换渲染前触发,如果缓存了要导航页面的内容则使用缓存,否则使用 pjax 加载
pjax:endnull, optionspjax 结束


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

赞 (6) or 分享 ()
游客 发表我的评论   换个身份
取消评论

表情
(1)个小伙伴在吐槽
  1. 好文#1
    游客2020-05-27 09:54 (3年前) 回复