最近搞了下列表页请求的功能,并做了一下调研整理,记此文备忘。

列表页请求的功能到处可见,比如在博客园。

点击相应的页码,页面返回相应的内容,看上去似乎大同小异,但是一些小的细节还是可以区分优劣。

full load

公司原来的代码采用的是 full load 的方式,也就是每点击一次,页面完全加载。并不只有我们网站这样做,很多大厂也这样搞,比如 新浪

列表页中的很多部分内容,其实都是一样的,这样做就每次需要重新加载这部分的内容,没有必要,而且 css、js 都需要重新加载(虽然可能有缓存)。以前我逛学校的论坛,是用 PHP 的 Discuz! 搭建的,每个主题后的回复页之间的跳转都是 full load 的方式,体验很差。

所以个人觉得,不管是性能还是用户体验上,full load 的方式在现在的 web 开发中,都是不可取的。

ajax

接着 ajax 出现了。ajax 就不多做介绍了,局部刷新,体验非常好。但是单纯的 ajax 虽然性能上比 full load 提高了不少,用户体验还不是很好,主要有以下两点。

  • 保存不了书签
  • 不支持浏览器的后退前进操作

究其根本,是因为传统的 ajax 操作不改变 url。

ajax + #

为了解决以上问题,聪明的开发者们用 # 来改善体验。

以博客园为例,我们请求第二页的时候,实际的 url 是 http://www.cnblogs.com/#p2,当点击页码发送请求时,同时改变页面的 url,因为改变的是 lcation.hash,所以页面并不会重载。我们将其保存为书签,当我们打开 http://www.cnblogs.com/#p2 时,我们可以提取 hash 值,据此发起相应的 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 搜索制定了一套规则。

  1. 网站提交 sitemap 给 Google;
  2. Google 发现 URL 里有 #! 符号,例如example.com/#!/detail/1,于是 Google 开始抓取 example.com/?_escaped_fragment_=/detail/1;

_escaped_fragment_ 这个参数是 Google 指定的命名,如果开发者希望把网站内容提交给 Google,就必须通过这个参数生成静态页面。

也就是说,每个 ajax 请求的内容,都需要提供一个相同内容的静态页面,供爬虫爬取。

随着 web 的发展,这一切已经成为了历史,现在的爬虫已经可以执行 JavaScript,爬取 ajax 请求的内容了!这部分的内容,就不展开了,有兴趣的可以参考下以下链接。

ajax + pushState

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.pushStatehistory.replaceState 以及 popstate 事件。

history.pushState 方法接受三个参数,依次为:

  • state:一个与指定网址相关的状态对象,popstate 事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填 null。history.state 属性能保存当前页面的 state 对象。
  • title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填 null。
  • url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。

假设现在的网页是 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 判断了)。

pushState vs #

ajax + pushState 以及 ajax + hash 的作用类似,但是推荐使用前者,有以下几个优势:

  • pushState 能改变的 URL 的范围大,在一个域名下的都可以,而 hash 的方法,因为 URL 只能改变 location.hash 的值,所以 URL 其实是只能在一个文档(document)下改变。比如 GitHub 中的路由,用 hash 去做,就会很麻烦,而且也很丑
  • 插入一条新的 history 记录,用 pushState,不一定要改变 URL,而 hash 必须改变当前的 URL(精确地说是当前文档的 hash 值)
  • 毫无疑问,我们需要把一些数据存储起来,在页面上提取,然后发起相应的 ajax。用 pushState 的方法,我们可以把数据存在 history.state 中,也可以根据 URL 去判断;而 hash 法只能改变 URL,根据 URL 判断(准确说是根据 hash 值判断)
  • 目前浏览器还不支持 pushState 的 title 参数,一旦支持,就可以被利用;而 hash 法是无法改变 title 的。

pjax

上面只是个简单的例子,如果要是实际生产环境中使用,大可用封装过的插件。

pjax = pushState + ajax,GitHub 使用的就是封装过的 pjax 插件。

使用方式可以参照相应的 README,不多做介绍了。

read more

浅析列表页请求优化(history API)的更多相关文章

  1. 关于帝国cms 列表页SEO优化的问题

    一般列表页面中,我们都需要带分页信息区分当前页号,为区分第一页,和第一页后的其他所有分页页面.我们推荐的做法为:第一页显示正常的标题,从第二页开始便显示xxxxx-第2页-xxxx网.做法是.修改帝国 ...

  2. 转: html5 history api详解~很好的文章

    从Ajax翻页的问题说起 请想象你正在看一个视频下面的评论,在翻到十几页的时候,你发现一个写得稍长,但非常有趣的评论.正当你想要停下滚轮细看的时候,手残按到了F5.然后,页面刷新了,评论又回到了第一页 ...

  3. 有关html5的history api

    从Ajax翻页的问题说起 请想象你正在看一个视频下面的评论,在翻到十几页的时候,你发现一个写得稍长,但非常有趣的评论.正当你想要停下滚轮细看的时候,手残按到了F5.然后,页面刷新了,评论又回到了第一页 ...

  4. HTML5 history API,创造更好的浏览体验

    HTML5 history API有什么用呢? 从Ajax翻页的问题说起 请想象你正在看一个视频下面的评论,在翻到十几页的时候,你发现一个写得稍长,但非常有趣的评论.正当你想要停下滚轮细看的时候,手残 ...

  5. 转:HTML5 History API 详解

    从Ajax翻页的问题说起 请想象你正在看一个视频下面的评论,在翻到十几页的时候,你发现一个写得稍长,但非常有趣的评论.正当你想要停下滚轮细看的时候,手残按到了F5.然后,页面刷新了,评论又回到了第一页 ...

  6. 大熊君学习html5系列之------History API(SPA单页应用的必备)

    一,开篇分析 Hi,大家好!大熊君又和大家见面了,(*^__^*) 嘻嘻……,这系列文章主要是学习Html5相关的知识点,以学习API知识点为入口,由浅入深的引入实例, 让大家一步一步的体会" ...

  7. HTML5 History API让ajax能回退到上一页

    HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL.这个功能很有用,例如通过一段JavaScript代码局部加载页面的内容,你希望通过改变当前页面的 ...

  8. 大熊君学习html5系列之------History API(SPA单页应用的必备------重构完结版)

    一,开篇分析 Hi,大家好!大熊君又和大家见面了,(*^__^*) 嘻嘻……,这系列文章主要是学习Html5相关的知识点,以学习API知识点为入口,由浅入深的引入实例, 让大家一步一步的体会" ...

  9. python测试开发django-23.admin列表页优化和排序

    前言 列表页优化和排序 ModelAdmin django的options.py里面 ModelAdmin类定义的参数可以设置admin后台列表页面,相关的参数如下 class ModelAdmin( ...

随机推荐

  1. ARTS打卡计划第一周-Algorithm

    7. Reverse Integer import math class Solution: def reverse(self, x: int) -> int: ret = 0 if x > ...

  2. Python学习—框架篇之初识Django

    什么是web框架? 框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来做表演. 对于所有的W ...

  3. windows server 2012R2 故障转移集群配置

    配置说明: AD:10.10.1.10/24  Node-2:10.10.1.20/24 Node-3:10.10.1.30/24 zhangsan-PC:10.10.1.50/24  VIP1:10 ...

  4. ios 11 SDK 新特性 使用

    Xcode 9虽然已经出了一段时间,但考虑到一些第三方库的适配,就没有升级.现在有时间了就升级到 Xcode 9,随便学习一下新的小技巧.感觉很好用哦~ 一.Named Color 关于更换主题的一个 ...

  5. cocoapods 安装中出的太多问题

    前言: 新欢的公司,新买的电脑,新安装 cocoapods.然后开开心心去百度如何安装 cocoapods,前面的步骤我就不说了. 在 pod setup 上之后,网速超慢然后就失败 fatal: T ...

  6. 在vue.js中使用echarts,数据动态刷新

    在vue使用echarts时,可能会遇到这样的问题,就是直接刷新浏览器,或者数据变化时,echarts不更新? 这是因为Echarts是数据驱动的,这意味着只要我们重新设置数据,那么图表就会随之重新渲 ...

  7. scrapy 爬取智联招聘

    准备工作 1. scrapy startproject Jobs 2. cd Jobs 3. scrapy genspider ZhaopinSpider www.zhaopin.com 4. scr ...

  8. MyEclipse 编译错误 web项目中的 js,jsp报错 更改

    搜索   validation   语法检测 必须 选定一个  不然不编译

  9. select2插件用法

    1.修改默认查询方法,使其可以根据value查询 this.element.select2({ allowClear: true, matcher: function (term, text, ele ...

  10. NumPy学习_01 ndarray相关概念

    1.NumPy库 NumPy = Numerical Python 是高性能科学计算和数据分析的基础库. pandas库充分借鉴了NumPy的相关概念,先行掌握NumPy库的用法,才能把pandas的 ...