一.Pjax是啥?
Pjax = history.pushState + Ajax
= history.pushState + Async JS + XML(xhr?)
BOM对象history被增强了一波,主要是对历史栈的操作,以前只有replace
, go
之类的,都会跳转并刷新整个页面,现在有了pushState
, replaceState
等等单纯操作历史栈的方法,只是单纯修改历史栈里的内容,没有副作用(页面不会跳转刷新)
关于history对象的更多信息请查看MDN History
二.Pjax有什么用?
1.最初的单页面应用(SPA)
页面刷新不仅浪费资源(很多同级页面上大部分内容都是相同的,没必要重新加载这些重复内容),还影响用户体验(loadingloadingloading…)。局部刷新能够避免loading影响用户体验,从Ajax概念一出来就有人开始这么做了,把整站做成单页面应用(Single Page App,简称SPA),Ajax请求JSON,再局部刷新呈现数据,最初的SPA存在很多缺点:
最大的问题:页面内容与URL不对应
这是致命的缺点,用户看到的内容与地址栏URL不对应,意味着内容无法分享传播(分享传播太重要了),浏览器前进/后退按钮也无法按照用户预期工作
其次:破坏SEO
纯Ajax实现的SPA对SEO有极大的消极影响,由于页面上很多内容都是Ajax请求之后js控制呈现的,蜘蛛看不到这些内容,也就根本不会被索引/收录
2.pretty AJAX URL
为了解决SEO问题,Google提出了一种很丑(虽然名字叫pretty AJAX URL。。)的方法:#!
据说Twitter用过一个月,后来用户反馈说太丑了,再后来就不用了。这种很丑的url确实能解决上述的第2个问题,但是需要搜索引擎配合,Google认可这种方式,Google的蜘蛛会把mydomain.com/index.html#!article1
和mydomain.com/index.html#!article2
当作两个不同的页面对待,服务给这种特殊URL的请求返回对应的页面即可,SEO问题没了。
但第一个问题还在,页面内容与URL仍然不对应,于是有了下面要说的Pjax
3.Pjax
W3C提出了新的API,增强了history对历史栈的控制能力,能够直接修改地址栏URL,直到这时页面内容与URL才终于能够对应了
每次局部刷新成功之后都调用history.pushState
同步更新地址栏的URL(维护历史栈),这些URL都对应可以直接访问的页面,每个局部刷新动作都是由a
标签触发的,js拦截默认跳转,再Ajax请求数据呈现数据,用户看到的是局部刷新和流畅的体验,蜘蛛看到的是普通的a
标签页面跳转,页面展示的内容始终与直接访问URL得到的内容一致,致命缺点也不存在了
如果各个浏览器支持性良好的话,Pjax能够完美支持单页面应用,如果浏览器不支持新API就把它当蜘蛛好了(目前好像没有好用的兼容方案,jq插件也无能为力),至于css/js冲突、内存泄露等等都是SPA本身的问题,成熟的SPA方案应该可以避免这些问题
三.Pjax怎么用?
核心是2个方法和1个事件:
history.pushState(state, title, url);
history.replaceState(state, title, url);
popstate
其中state
参数是自定义obj,可以通过history.state
拿到,title参数没什么用,浏览器都不认。push
和replace
的区别是前者把指定url入栈,后者换掉栈顶元素。浏览器的前进/后退会触发popstate
,可以在里面根据history.state
数据作出相应处理
前辈有一个比较简洁的DEMO:ajax与HTML5 history pushState/replaceState实例
此外,也有相应的开源库可以使用:
jQuery的pjax:最近(2015-9-22)还在更新
history.js:2年前停止更新了,据说很好用
welefen/pjax:主流js库的Pjax用法
四.为什么要用Pjax?
在不影响SEO的前提下,局部刷新 + 本地缓存能够带来前所未有的快速体验,这是Pjax的绝对优势
Pjax适用于非纯移动端的单页面应用,能够实现流畅的用户体验,而且封装好的Pjax组件支持一些额外的功能,比如缓存、本地存储、动画等等,使用起来也很方便
移动页面一般不存在频繁地大篇幅页面内容更新,而且移动页面很少需要考虑SEO,直接用Ajax即可
五.Pjax案例
- 全站Pjax博客:porridge