`

<转载>PJAX的实现与应用实例

 
阅读更多

转载出处: PJAX的实现与应用实例 wzw at 2013-12-04 20:27:53 

 

一、前言

web发展经历了一个漫长的周期,最开始很多人认为Javascript这们语言是前端开发的累赘,是个鸡肋,那个时候人们还享受着从一个a链接蹦到另一个页面的web神奇魔术。后来随着JavaScript的不断更新换代,他的功能不仅仅是为网页添加一点特效了,语言本身的加强以及对DOM操作能力的提升让他在前端大放光彩。尤其是ajax的出现,让JavaScript以及整个web的发展翻开了崭新的一页。

利用ajax局部刷新页面,相信很多人玩得相当熟练了。如果整个页面的刷新都是使用ajax,我们可以称之为一个webapp,所有的逻辑都是在当页处理,这种形式的页面带来的体验是十分不错的,减少了那些比较“冗余”的页面跳转、新开页面等。不过,webapp的代码是十分不好维护的,页面逻辑太多太深,出点小问题,整个页面就会瘫痪,而且不方便定位bug,可维护性很低。

二、PJAX的实现与应用

1.场景再现-ajax弊端

ajax是一个非常好玩的小东西,不过用起来也会存在一些问题。

我们可以利用ajax进行无刷新改变文档内容,但是没办法去修改URL,有童鞋要问,这里为什么一定要修改URL呢?一个URL代表一个特定的网络资源,ajax修改了页面的内容,所以用不同的URL去标识他们,这个还是挺有必要的。

比如我们设计了一个单词查询的页面,比较合理的UR应该是http://example.com/word,不同的word对应不同的内容,但是如果整个页面都是ajax实现,我们就没法去修改/word了,当然我们可以使用hash如http://example.com#word,但这样就不能很好的处理浏览器的前进和后退问题。如:在页面中查询了单词A的翻译,接着又查询了单词B,这个时候浏览器的浏览历史会生成http://example.com#A和http://example.com#B两个记录,可是当我们从B转回A的时候,AJAX的效果还停留在B的状态(页面显示的还是单词B的翻译)。部分浏览器对此问题引入了onhashchange的接口,只要URL的hash值发生变化,我们的程序就可以监听并做出相应。不过对于那些木有这个接口的浏览器,就得定时去判断hash的变化了。

而这样的方式对搜索引擎是十分不友好的,twitter和google约定使用hash bang (#!xxx),也就是hash后面的第一个字符为感叹号,这样的网址他们是会爬取的,但是其他搜索引擎不支持。PJAX可以在改变页面内容的同时也改变他的URL,下面来说说PJAX和他的应用。

2.什么是PJAX

history API中有几个新特性,分别是history.pushState和history.replaceState,我们把pushState+AJAX进行封装,合起来简单点叫,就是PJAX~ 虽说实现技术上没什么新东西,但是概念上还是有所不同的。

PJAX的基本思路是,用户点击一个链接,通过ajax更新页面变化的部分,然后使用HTML5的pushState修改浏览器的URL地址,这样有效地避免了整个页面的重新加载。如果浏览器不支持history的两个新API或者JS被禁用了,那这个链接就只能跳转并重新刷新整个页面了。和传统的ajax设计稍微不同,ajax通常是从后台获取JSON数据,然后由前端解析渲染,而PJAX请求的是一个在服务器上生成好的HTML碎片,如下图所示:

客户端向服务器发送一个普通的请求(1),其实也就是点击了一个链接,服务器会相应这个请求(2),返回一个html文档。客户端向服务器发送一个有PJAX标志的请求(3),此时服务器只返回一个html碎片(4)。但是这两次请求都让客户端的URL变化了,希望上面的说明可以让你明白了PAJX和AJAX的区别了。

3.PJAX的实现

先看一个小DEMO吧,这个DEMO也写了我半个多小时,看之前先说明一下,打开你的现代浏览器(chrome,Firefox,opera,IE9+等),进入gallery页面,查看图片的时候注意观察浏览器的title和url变化,点击前进后退按钮也注意查看其变化。我已经在浏览历史管理中push了三条历史记录。

DEMO地址:http://qianduannotes.duapp.com/demo/PJAX/index.html

如果你还没有理解上面说的PJAX和AJAX的区别,看完这个demo,你应该有所领悟吧!在URL变化之后,页面并没有刷新,而是继续完成自己的动画(demo中为fadeOut)。

在HTML4,Histroy对象有下面属性方法:

  • length:历史堆栈中的记录数。

  • back():返回上一页。

  • forward():前进到下一页。

  • go([delta]):delta是个数字,如果不写或为0,则刷新本页;如果为正数,则前进到相应数目的页面;若为负数,则后退到相应数目的页面。

在HTML5中,新增了两个方法:

  • pushState(data, title [, url]):往历史堆栈的顶部添加一条记录。data为一个对象或null,它会在触发window的popstate事件(window.onpopstate)时,作为参数的state属性传递过去;title为页面的标题,但当前所有浏览器都忽略这个参数;url为页面的URL,不写则为当前页。

  • replaceState(data, title [, url]):更改当前页面的历史记录。参数同上。这种更改并不会去访问该URL。

当点击“上一张”、“下一张”这两个链接的时候,首先通过pushState修改URL以及修改document.title,那这个时候你就可以当做文档已经进入了另外一个链接了,然后该干什么干什么。demo中是让图片fadeOut,fadeOut完了之后让浏览器去加载资源,这个步骤就是正常的AJAX操作啦,没有什么特殊之处了~

因为只准备了三张图片,所有后台写的也比较简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?phperror_reporting(false);$num $_GET['num'];if(array_key_exists('HTTP_X_PJAX'$_SERVER) && $_SERVER['HTTP_X_PJAX'] === 'true'){    if($num == 1) {?>
        <div class="imgwrap">
            <img src="./images/1.jpg" />
        </div>
        <span><a href="num=2" class="next">下一张&gt;&gt;</a></span>
<?php
    else if ($num == 2) {?>
        <div class="imgwrap">
            <img src="./images/2.jpg" />
        </div>
        <span><a href="num=1" class="previous">&lt;&lt;上一张</a>
        <a href="num=3" class="next">下一张&gt;&gt;</a></span>
<?php
    else {?>
        <div class="imgwrap">
            <img src="./images/3.jpg" />
        </div>
        <span><a href="num=2" class="previous">&lt;&lt;上一张</a></span>
<?php
    }
}?>

上面那张图中,我们看到了,并不是每个连接都使用PJAX来加载,如果有X_PJAX标识,我们才会添加相应的处理。js中稍加注意可以看到:

1
2
3
4
5
6
7
8
9
10
$.ajax({
   "url""./interface.php",
   "data": {
       "num": num
   },
   "dataType""html",
   "headers": {
       "X_PJAX": true
   }
});

请求中:

1
2
3
4
5
6
7
Accept:text/html, */*; q=0.01
Accept-Encoding:gzip,deflate,sdch
Connection:keep-alive
Host:qianduannotes.duapp.com
User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
X-Requested-With:XMLHttpRequest
X_PJAX:true

我在请求的header中加了一个X_PJAX的头,而后台在处理的时候也做了判断:

1
2
3
4
function is_pjax(){
    return array_key_exists('HTTP_X_PJAX'$_SERVER
            && $_SERVER['HTTP_X_PJAX'] === 'true';
}

并不是一定要求在header头部中加入X_PJAX的信息,你也可以在url中加入相关的参数,比如:http://example.com?pjax=1,或者其他方式,只要前后端达到一个共识就行。

三、开源的PJAX库

已经有人对这个东西做了封装,我就不重复造轮子了。

  • welefen封装的库,对jquery、qwrap和kissy都做了封装,github地址

  • Yahoo团队 PJAX地址

并不是页面中所有的链接都需要使用PJAX加载,所有在需要这个东西的a标签上加一个属性,如data-pjax=true,然后统一添加事件。

四、注意事项

  • 如果浏览器不支持pushState接口函数,那就只能退化为ajax或者使用hash bang了~

  • 本地环境下使用的话,浏览器会报错:`Uncaught SecurityError: A history state object with URL file:///E:/baidu_app/demo/PJAX/pic-2' cannot be created in a document with origin 'null'. ,所以如果你要测试的话,请把代码丢到服务器上!

  • 为了得到更好的体验,PJAX经常配合localStorage来使用,把请求到的内容缓存到本地,再一次请求的时候先从本地查看。如果你的内容是动态变化的,缓存的时候加一个缓存时间,方便更新缓存。

  • 还有一个容易忽略的东西是统计,使用PJAX只会局部刷新页面,如果忽略了对统计函数的更新,那就会让你失去很多数据。

五、参考资料

分享到:
评论

相关推荐

    C# 解析json格式数据为IList

    4. 现在,`users`变量就是一个IList&lt;User&gt;实例,你可以像操作任何其他C#集合一样操作它,例如遍历或查找特定项。 如果你的JSON数据结构复杂,包含嵌套的对象或数组,Json.NET也能轻松处理。它支持自定义转换器、...

    C#源码大集合 03(共3卷)

    chap09-多线程&lt;br&gt;├─D00-多线程&lt;br&gt;├─Windows多线程编程技术与实例&lt;br&gt;...&lt;br&gt;...&lt;br&gt;├─多线程,多接收模式串口类&lt;br&gt;├─多线程文件传输&lt;br&gt;├─多线程的日志记录DLL&lt;br&gt;├─多线程端口扫描程序&lt;br&gt;├─多...

    Visual C++网络通信编程实用案例精选_9(全)

    116&lt;br&gt;4.2.1 实现原理 116&lt;br&gt;4.2.2 实例实现 117&lt;br&gt;4.3 将应用程序加入到IE工具栏 121&lt;br&gt;4.3.1 实现原理 122&lt;br&gt;4.3.2 实例实现 123&lt;br&gt;4.4 超级链接的实现 127&lt;br&gt;4.4.1 实现原理 127&lt;br&gt;4.4.2 ...

    Visual C++网络通信编程实用案例精选_6

    116&lt;br&gt;4.2.1 实现原理 116&lt;br&gt;4.2.2 实例实现 117&lt;br&gt;4.3 将应用程序加入到IE工具栏 121&lt;br&gt;4.3.1 实现原理 122&lt;br&gt;4.3.2 实例实现 123&lt;br&gt;4.4 超级链接的实现 127&lt;br&gt;4.4.1 实现原理 127&lt;br&gt;4.4.2 ...

    JSP网络编程从基础到实践

    《JSP网络编程从基础到实践》&lt;br&gt; 实例1 第一个JSP页面&lt;br&gt;Web开发基础&lt;br&gt; 实例2 HTML与JavaScript交互示例&lt;br&gt;JSP语法&lt;br&gt; 实例3 JSP程序的基本结构&lt;br&gt; 实例4 简单数据类型综合应用实例&lt;br&gt; 实例5 包装类综合应用...

    c#基础实例

    实例5 Ftp服务器端实现&lt;br&gt; 实例6 Ftp客户端实现&lt;br&gt; 第七章 WEB 编程应用实例&lt;br&gt; 实例1 一个ASP.NET示例程序&lt;br&gt; 实例2 Calendar控件应用举例——网络日历&lt;br&gt; 实例3 Validation 控件应用举例——输入有效性的检测...

    C#.net_经典编程例子400个

    第1章 窗体与界面设计 1&lt;br&gt;1.1 菜单应用实例 2&lt;br&gt;实例001 带历史信息的菜单 2&lt;br&gt;实例002 菜单动态合并 3&lt;br&gt;实例003 像开始菜单一样漂亮的菜单 4&lt;br&gt;实例004 任务栏托盘菜单 5&lt;br&gt;实例005 可以拉伸...

    J2EE经典实例详解 <2>

    &lt;br&gt;&lt;br&gt;本书紧紧围绕Duke应用实例,进行了由远及近的全面介绍和自底向上的层层分析,使读者能在一个较短时间内很快了解和掌握开发大型企业的Web应用技术。&lt;br&gt;&lt;br&gt;本书从最后端的数据分析入手,进而详尽介绍和分析...

    《Visual.C#.编程精彩百例》配套光盘part1

    &lt;br&gt;实例39 单个线程同步运行 &lt;br&gt;实例40 多线程同步运行 &lt;br&gt;实例41 线程Thread Relative Static跟踪与实现 &lt;br&gt;实例42 线程池(ThreadPool)的应用 &lt;br&gt;实例43 多线程互斥运行 &lt;br&gt;实例44 多线程时钟应用程序 &lt;br&gt;...

    asp.net程序开发范例宝典10

    第10章 视图、存储过程及触发器的应用 427&lt;br&gt;10.1 视图的应用 428&lt;br&gt;实例238 在ASP.NET中应用视图 428&lt;br&gt;实例239 获取数据库中的全部用户视图 429&lt;br&gt;实例240 通过视图修改数据 430&lt;br&gt;10.2 存储过程...

    Delphi7 编程 100 实例

    ToolBar工具栏控件的使用&lt;br&gt;动态建立主菜单选项&lt;br&gt;窗口界面的动态分隔条&lt;...应用程序通用功能列表&lt;br&gt;OleContainer——OLE容器&lt;br&gt;将程序项设置到控制面板&lt;br&gt;艺术化排列桌面图标&lt;br&gt;Delphi中的ini文件的读写&lt;br&gt;建立...

    数据结构算法与应用 很详细的,数据结构算法全集 这个是你想找的

    目 录&lt;br&gt;译者序&lt;br&gt;前言&lt;br&gt;第一部分 预备知识&lt;br&gt;第1章 C++程序设计 1&lt;br&gt;1.1 引言 1&lt;br&gt;1.2 函数与参数 2&lt;br&gt;1.2.1 传值参数 2&lt;br&gt;1.2.2 模板函数 3&lt;br&gt;1.2.3 引用参数 3&lt;br&gt;1.2.4 常量引用参数 4&lt;br&gt;1.2.5 ...

    C#程序开发范例宝典4

    178&lt;br&gt;实例122 图像旋转 181&lt;br&gt;实例123 鼠标拖拽图像 182&lt;br&gt;4.4 图形缩放与变换 183&lt;br&gt;实例124 如何放大和缩小图像 183&lt;br&gt;实例125 如何旋转JPG图像 184&lt;br&gt;实例126 如何实现图形翻转 185&lt;br&gt;4.5 ...

    C源代码实例集

    &lt;br&gt;第三部分 数值计算与趣味数学篇&lt;br&gt; &lt;br&gt;075 绘制余弦曲线和直线的迭加&lt;br&gt;076 计算高次方数的尾数 &lt;br&gt;077 打鱼还是晒网 &lt;br&gt;078 怎样存钱以获取最大利息 &lt;br&gt;079 阿姆斯特朗数 &lt;br&gt;080 亲密数 &lt;br&gt;081 自守数 ...

    C#.NET案例开发集锦代码7-11章

    第七章&lt;br&gt;案例1通过委托实现异步调用&lt;br&gt;案例2使用定时器执行方法&lt;br&gt;案例3控制线程状态&lt;br&gt;案例4多线程同步的实现&lt;br&gt;案例5结束进程&lt;br&gt;案例6多线程资源共享与访问&lt;br&gt;案例7仅运行一个应用程序实例&lt;br&gt;案例7如何...

    C#程序开发范例宝典5part3

    240&lt;br&gt;实例162 检测是否安装声卡 240&lt;br&gt;实例163 打开和关闭CDROM 241&lt;br&gt;实例164 控制PC喇叭发声 242&lt;br&gt;5.5 多媒体应用 243&lt;br&gt;实例165 开机祝福程序 243&lt;br&gt;实例166 制作家庭影集 245&lt;br&gt;实例167 ...

    asp.net程序开发范例宝典12

    494&lt;br&gt;12.2 对文件夹的操作 495&lt;br&gt;实例274 创建、移动和删除文件夹 495&lt;br&gt;实例275 修改文件夹名称 497&lt;br&gt;实例276 查看文件夹是否存在 498&lt;br&gt;12.3 文件上传与下载 499&lt;br&gt;实例277 单文件上传 500&lt;br...

    c#网络应用编程相关学习资料

    C#网络应用编程。&lt;br&gt;第一部份:网络编程基础。...使用线程&lt;br&gt;10.IP组播&lt;br&gt;第三部分 应用层的编程实例&lt;br&gt;11.ICMP&lt;br&gt;12.SNMP&lt;br&gt;13.SMTP&lt;br&gt;14.HTTP&lt;br&gt;15.活动目录&lt;br&gt;16.远程技术&lt;br&gt;17.网络安全

    mapx原理及使用+项目实例

    目 录&lt;br&gt;第一章 GIS开发方式选择 3&lt;br&gt;一、 应用型GIS开发的三种实现方式 3&lt;br&gt;1、 独立开发 3&lt;br&gt;2、 单纯二次开发 3&lt;br&gt;3、 集成二次开发 3&lt;br&gt;二、 三种实现方式的分析与比较 3&lt;br&gt;第二章 组件式GIS与MAPX 4&lt;br&gt;...

Global site tag (gtag.js) - Google Analytics