最近搞了下列表页请求的功能,并作了一下调研整理,记此文备忘。javascript
列表页请求的功能处处可见,好比在博客园。css
点击相应的页码,页面返回相应的内容,看上去彷佛大同小异,可是一些小的细节仍是能够区分优劣。html
公司原来的代码采用的是 full load 的方式,也就是每点击一次,页面彻底加载。并不仅有咱们网站这样作,不少大厂也这样搞,好比 新浪。html5
列表页中的不少部份内容,其实都是同样的,这样作就每次须要从新加载这部分的内容,没有必要,并且 css、js 都须要从新加载(虽然可能有缓存)。之前我逛学校的论坛,是用 PHP 的 Discuz! 搭建的,每一个主题后的回复页之间的跳转都是 full load 的方式,体验不好。java
因此我的以为,不论是性能仍是用户体验上,full load 的方式在如今的 web 开发中,都是不可取的。jquery
接着 ajax 出现了。ajax 就很少作介绍了,局部刷新,体验很是好。可是单纯的 ajax 虽然性能上比 full load 提升了很多,用户体验还不是很好,主要有如下两点。git
究其根本,是由于传统的 ajax 操做不改变 url。github
为了解决以上问题,聪明的开发者们用 # 来改善体验。web
以博客园为例,咱们请求第二页的时候,实际的 url 是 http://www.cnblogs.com/#p2,当点击页码发送请求时,同时改变页面的 url,由于改变的是 lcation.hash,因此页面并不会重载。咱们将其保存为书签,当咱们打开 http://www.cnblogs.com/#p2 时,咱们能够提取 hash 值,据此发起相应的 ajax 请求。ajax
接着咱们来看第二个需求,如何支持浏览器的后退前进操做。有些童鞋可能会问,已经有了上一页、下一页的功能,支持浏览器的后退前进操做,有必要吗?灰常有必要,好比咱们先点了第二页,而后点了第四页,我须要回到第二页,又忘了刚才点的是第几页,会条件反射地去点浏览器的回退。其实博客园 http://www.cnblogs.com/ 没有作这个功能,如何支持?咱们能够监听 hashchange
事件,当 url 的 hash 值发生变化时,从新发送请求。可是 hashchange
事件并不支持某些 IE (http://caniuse.com/#search=hashchange),对于不支持的浏览器,咱们只能设置一个定时器,不断得去查看页面的 hash 是否改变,会形成不小的性能问题(或者直接放弃这部分浏览器,或者降级处理)。
简单地写了个 demo,猛戳 https://github.com/hanzichi/practice/tree/master/2016/pjax/hash 查看(没有兼容不支持 hashchange 事件的浏览器)。
这里再插点题外话,讲点小历史。之前的搜索引擎爬虫,是不会抓取 ajax 请求的内容的(毕竟木有这么智能),只会去抓网页的源代码,这就蛋疼了,咱们既但愿用 ajax 改善体验,又但愿内容能够被搜索引擎爬虫抓取,两者不可得兼?Google 搜索制定了一套规则。
_escaped_fragment_ 这个参数是 Google 指定的命名,若是开发者但愿把网站内容提交给 Google,就必须经过这个参数生成静态页面。
也就是说,每一个 ajax 请求的内容,都须要提供一个相同内容的静态页面,供爬虫爬取。
随着 web 的发展,这一切已经成为了历史,如今的爬虫已经能够执行 JavaScript,爬取 ajax 请求的内容了!这部分的内容,就不展开了,有兴趣的能够参考下如下连接。
ajax + #,彷佛能够知足通常的需求了,可是若是不止限于列表请求呢?改变 hash 值搞的 URL 看起来不像一个正常的 URL,并且 hash 原本的用处并不是如此,这样搞有点黑科技的感受。HTML5 的出现,能让 ajax 变的更加优雅。
为了解决传统的 ajax 带来的问题,HTML5 里增强了 history API,加入了 pushState、replaceState 接口和 popstate 事件。
举个简单的例子,咱们看 GitHub,首先定位到页面 https://github.com/hanzichi/underscore-analysis,而后点击该 repo 下第一个行第一个文件夹 『underscore-1.8.3.js』,URL 变为 https://github.com/hanzichi/underscore-analysis/tree/master/underscore-1.8.3.js,页面局部刷新,看了下 Network 面板,是一个 ajax 请求,且该操做支持保存书签、回退前进等功能。这一切的实现都基于 history 新增的 API。
history 原有的 API 大都灰常简单,好比 history.length
(该 tab 访问过的网页数量,新建 tab 时的空标签该属性值为 1),history.back()
,history.forward()
,history.go(-1)
等等,很少加介绍,简单介绍下新增的 history.pushState
,history.replaceState
以及 popstate
事件。
history.pushState 方法接受三个参数,依次为:
假设如今的网页是 http://localhost/1.htm,咱们使用 pushState 方法在浏览记录(history 对象)中添加一个新纪录。
var stateObj = {page: 2 }; history.pushState(stateObj, "page 2", "2.htm");
浏览器地址栏马上显示 <localhost/2.htm>,可是并不会跳到 2.htm 的页面(pushState 不会触发页面刷新),甚至这个页面不存在也不会报错,它只是成为了浏览器中的最新记录,能够查看 history.length,会发现该属性值增长了 1。若是这时点击倒退,url 将显示 1.htm,内容不变。
若是 pushState 的 url 参数,设置了一个当前网页的 # 值(hash),并不会触发 hashchange 事件。若是设置了一个非同域的网址,则会报错。
history.replaceState 方法的参数和 pushState 同样,区别是它修改浏览器历史中当前页面的值,即便用 replaceState,history.length 并不会增长,只是替换了当前页面在 history 中的记录。
history.pushState({page: 1}, "title 1", "?page=1"); history.pushState({page: 2}, "title 2", "?page=2"); history.replaceState({page: 3}, "title 3", "?page=3"); history.back(); // url 显示为http://example.com/example.html?page=1 history.back(); // url 显示为http://example.com/example.html history.go(2); // url 显示为http://example.com/example.html?page=3
前面 ajax + # 的例子中,咱们用 hashchange 去判断浏览器的前进后退操做,那么,是否有原生的监听浏览器前进回退操做的事件呢?有的,popstate 事件。每当同一个文档的浏览历史(即 history 对象)出现变化时,就会触发 popstate 事件,只有当用户手动点击浏览器后退前进按钮,或者调用 back、forward、go 方法时才会触发。
window.onpopstate = function(e) { console.log(e.state); // 等价于 // console.log(history.state); }
因而咱们要实现一个列表页请求的功能,就呼之欲出了。点击页码,用 pushState 塞入一条新的记录,同时改变 url,而后发送 ajax 请求,局部更新。点击浏览器后退前进按钮,触发 popstate 事件,发送请求,局部更新,请求的字段,能够根据 url 去判断,也能够储存在 state 中。写了个简单的 demo,猛戳 https://github.com/hanzichi/practice/tree/master/2016/pjax/pushState 查看(根据 url 判断了)。
ajax + pushState 以及 ajax + hash 的做用相似,可是推荐使用前者,有如下几个优点:
上面只是个简单的例子,若是要是实际生产环境中使用,大可用封装过的插件。
pjax = pushState + ajax,GitHub 使用的就是封装过的 pjax 插件。
使用方式能够参照相应的 README,很少作介绍了。