热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

jspjax和window.history.pushState,replaceState

原文:http:blog.linjunhalida.comblogpjaxgithub:https:github.comdefunktjquery-pjax

原文:http://blog.linjunhalida.com/blog/pjax/

github:https://github.com/defunkt/jquery-pjax

 

什么是pjax?

现在很多网站(facebook, twitter)都支持这样的一种浏览方式, 当你点击一个站内的链接的时候, 不是做页面跳转, 而是只是站内页面刷新。 这样的用户体验, 比起整个页面都闪一下来说, 好很多。

其中有一个很重要的组成部分, 这些网站的ajax刷新是支持浏览器历史的, 刷新页面的同时, 浏览器地址栏位上面的地址也是会更改, 用浏览器的回退功能也能够回退到上一个页面。

那么如果我们想要实现这样的功能, 我们如何做呢?

我发现pjax提供了一个脚本支持这样的功能。

pjax项目地址在 https://github.com/defunkt/jquery-pjax 。 实际的效果见: http://pjax.heroku.com/ 没有勾选pjax的时候, 点击链接是跳转的。 勾选了之后, 链接都是变成了ajax刷新。

为什么要用pjax?

pjax有好几个好处:

  • 用户体验提升。

    页面跳转的时候人眼需要对整个页面作重新识别, 刷新部分页面的时候, 只需要重新识别其中一块区域。自从我在自己的网站 GuruDigger 上面采用了pjax技术后, 不由觉得访问其他只有页面跳转的网站难受了许多。 同时, 由于刷新部分页面的时候提供了一个loading的提示, 以及在刷新的时候旧页面还是显示在浏览器中, 用户能够容忍更长的页面加载时间。

  • 极大地减少带宽消耗和服务器消耗。

    由于只是刷新部分页面, 大部分的请求(css/js)都不会重新获取, 网站带有用户登录信息的外框部分都不需要重新生成了。 虽然我没有具体统计这部分的消耗, 我估计至少有40%以上的请求, 30%以上的服务器消耗被节省了。

坏处我觉得也有:

  • IE6等历史浏览器的支持

    虽然我没有实际测试, 但是由于pjax利用到了新的标准, 旧的浏览器兼容会有问题。 不过pjax本身支持fallback, 当发现浏览器不支持该功能的时候, 会回到原始的页面跳转上面去。

  • 复杂的服务器端支持

    服务器端需要根据过来的请求, 判断是作全页面渲染还是部分页面渲染, 相对来说系统复杂度增大了。 不过对于设计良好的服务器代码, 支持这样的功能不会有太大的问题。

综合起来, 由于用户体验和资源利用率的提升, 坏处是可以完全得到弥补的。 我强烈推荐大家使用。

如何使用pjax?

直接看 官方文档 就可以了。

我觉得做技术的人要养成看一手的技术资料的习惯。

有一个rails针对pjax的 gem插件 可以直接使用。 也有 django的支持 。

pjax的原理

为了能够处理问题, 我们需要能够理解pjax的运作方式。 pjax的代码只有一个文件: https://github.com/defunkt/jquery-pjax/blob/master/jquery.pjax.js

如果有能力, 可以自己去看一遍。 我这里解释一下原理。

首先, 我们在html里面指定, 需要做pjax的链接内容是哪些, 以及点击之后需要更新的部分(放在data-pjax属性里面):

1
$('a[data-pjax]').pjax() 

当加载了pjax脚本之后, 它会拦截这些链接的事件, 然后包装成一个ajax请求, 发送给服务器。

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 
$.fn.pjax = function( container, options ) {  return this.live('click.pjax', function(event){  handleClick(event, container, options)  }) }  function handleClick(event, container, options) {  $.pjax($.extend({}, defaults, options))  ...  event.preventDefault() } var pjax = $.pjax = function( options ) {  ...  pjax.xhr = $.ajax(options) } 

这个请求带有X-PJAX的HEADER标识, 服务器在收到这样的请求的时候, 就知道只需要渲染部分页面返回就可以了。

1
2 
xhr.setRequestHeader('X-PJAX', 'true') xhr.setRequestHeader('X-PJAX-Container', context.selector) 

pjax接受到返回的请求之后, 更新data-pjax指定的区域, 同时也会更新浏览器的地址。

1
2 3 4 5 6 
options.success = function(data, status, xhr) {  var container = extractContainer(data, xhr, options)  ...  if (container.title) document.title = container.title  context.html(container.contents) } 

为了能够支持浏览器的后退, 利用到了history的api, 记录下来对应的信息,

1
2 3 4 5 6 7 8 9 10 11 
pjax.state = {  id: options.id || uniqueId(),  url: container.url,  container: context.selector,  fragment: options.fragment,  timeout: options.timeout }  if (options.push || options.replace) {  window.history.replaceState(pjax.state, container.title, container.url) } 

当浏览器后退的时候, 拦截事件, 根据记录的历史信息, 产生一个新的ajax请求。

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 
$(window).bind('popstate', function(event){  var state = event.state  if (state && state.container) {  var container = $(state.container)  if (container.length) {  ...  var options = {  id: state.id,  url: state.url,  container: container,  push: false,  fragment: state.fragment,  timeout: state.timeout,  scrollTo: false  }   if (contents) {  // pjax event is deprecated  $(document).trigger('pjax', [null, options])  container.trigger('pjax:start', [null, options])  // end.pjax event is deprecated  container.trigger('start.pjax', [null, options])   container.html(contents)  pjax.state = state   container.trigger('pjax:end', [null, options])  // end.pjax event is deprecated  container.trigger('end.pjax', [null, options])  } else {  $.pjax(options)  }  ...  }  } } 

为了支持fallback, 一个是在加载的时候判断浏览器是否支持history push state API:

1
2 3 4 5 
// Is pjax supported by this browser? $.support.pjax =  window.history && window.history.pushState && window.history.replaceState  // pushState isn't reliable on iOS until 5.  && !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/) 

另一个是当发现请求一段时间没有回复的时候(可以设置参数timeout), 直接做页面跳转。

1
2 3 4 5 6 7 8 9 
options.beforeSend = function(xhr, settings) {  if (settings.timeout > 0) {  timeoutTimer = setTimeout(function() {  if (fire('pjax:timeout', [xhr, options]))  xhr.abort('timeout')  }, settings.timeout)   // Clear timeout setting so jquerys internal timeout isn't invoked  settings.timeout = 0 

结论

既然都看到这里了, 你为什么不去实际使用一下pjax呢? 有那么多好处, 我觉得几乎所有网站都应该采用pjax。 赶紧用起来吧!

 

 

 

原文:https://www.studyscript.com/Post/index/id/3018.html?page=3

正文~

 

概述

浏览器窗口有一个history对象,用来保存浏览历史。

 

如果当前窗口先后访问了三个网址,那么history对象就包括三项,history.length属性等于3。

 

history.length // 3

history对象提供了一系列方法,允许在浏览历史之间移动。

 

back():移动到上一个访问页面,等同于浏览器的后退键。

forward():移动到下一个访问页面,等同于浏览器的前进键。

go():接受一个整数作为参数,移动到该整数指定的页面,比如go(1)相当于forward(),go(-1)相当于back()。

history.back();

history.forward();

history.go(-2);

如果移动的位置超出了访问历史的边界,以上三个方法并不报错,而是默默的失败。

 

history.go(0)相当于刷新当前页面。

 

history.go(0);

常见的“返回上一页”链接,代码如下。

 

document.getElementById('backLink').Onclick= function () {

  window.history.back();

}

注意,返回上一页时,页面通常是从浏览器缓存之中加载,而不是重新要求服务器发送新的网页。

 

history.pushState()

HTML5为history对象添加了两个新方法,history.pushState()和history.replaceState(),用来在浏览历史中添加和修改记录。

 

if (!!(window.history && history.pushState)){

  // 支持History API

} else {

  // 不支持

}

上面代码可以用来检查,当前浏览器是否支持History API。如果不支持的话,可以考虑使用Polyfill库History.js。

 

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

 

state:一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填null。

title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填null。

url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。

假定当前网址是example.com/1.html,我们使用pushState方法在浏览记录(history对象)中添加一个新记录。

 

var stateObj = { foo: 'bar' };

history.pushState(stateObj, 'page 2', '2.html');

添加上面这个新记录后,浏览器地址栏立刻显示example.com/2.html,但并不会跳转到2.html,甚至也不会检查2.html是否存在,它只是成为浏览历史中的最新记录。假定这时你访问了google.com,然后点击了倒退按钮,页面的url将显示2.html,但是内容还是原来的1.html。你再点击一次倒退按钮,url将显示1.html,内容不变。

 

总之,pushState方法不会触发页面刷新,只是导致history对象发生变化,地址栏会有反应。

 

如果pushState的url参数,设置了一个新的锚点值(即hash),并不会触发hashchange事件。如果设置了一个跨域网址,则会报错。

 

// 报错

history.pushState(null, null, 'https://twitter.com/hello');

上面代码中,pushState想要插入一个跨域的网址,导致报错。这样设计的目的是,防止恶意代码让用户以为他们是在另一个网站上。

 

history.replaceState()

history.replaceState方法的参数与pushState方法一模一样,区别是它修改浏览历史中当前纪录。

 

假定当前网页是example.com/example.html。

 

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

history.state属性

history.state属性返回当前页面的state对象。

 

history.pushState({page: 1}, 'title 1', '?page=1');

 

history.state

// { page: 1 }

popstate事件

每当同一个文档的浏览历史(即history对象)出现变化时,就会触发popstate事件。

 

需要注意的是,仅仅调用pushState方法或replaceState方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用Javascript调用back、forward、go方法时才会触发。另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。

 

使用的时候,可以为popstate事件指定回调函数。这个回调函数的参数是一个event事件对象,它的state属性指向pushState和replaceState方法为当前URL所提供的状态对象(即这两个方法的第一个参数)。

 

window.Onpopstate= function (event) {

  console.log('location: ' + document.location);

  console.log('state: ' + JSON.stringify(event.state));

};

 

// 或者

 

window.addEventListener('popstate', function(event) {

  console.log('location: ' + document.location);

  console.log('state: ' + JSON.stringify(event.state));

});

上面代码中的event.state,就是通过pushState和replaceState方法,为当前URL绑定的state对象。

 

这个state对象也可以直接通过history对象读取。

 

var currentState = history.state;

注意,页面第一次加载的时候,在load事件发生后,Chrome和Safari浏览器(Webkit核心)会触发popstate事件,而Firefox和IE浏览器不会。

 

URLSearchParams API

URLSearchParams API用于处理URL之中的查询字符串,即问号之后的部分。没有部署这个API的浏览器,可以用url-search-params这个垫片库。

 

var paramsString = 'q=URLUtils.searchParams&topic=api';

var searchParams = new URLSearchParams(paramsString);

URLSearchParams有以下方法,用来操作某个参数。

 

has():返回一个布尔值,表示是否具有某个参数

get():返回指定参数的第一个值

getAll():返回一个数组,成员是指定参数的所有值

set():设置指定参数

delete():删除指定参数

append():在查询字符串之中,追加一个键值对

toString():返回整个查询字符串

var paramsString = 'q=URLUtils.searchParams&topic=api';

var searchParams = new URLSearchParams(paramsString);

 

searchParams.has('topic') // true

searchParams.get('topic') // "api"

searchParams.getAll('topic') // ["api"]

 

searchParams.get('foo') // null,注意Firefox返回空字符串

searchParams.set('foo', 2);

searchParams.get('foo') // 2

 

searchParams.append('topic', 'webdev');

searchParams.toString() // "q=URLUtils.searchParams&topic=api&foo=2&topic=webdev"

 

searchParams.append('foo', 3);

searchParams.getAll('foo') // [2, 3]

 

searchParams.delete('topic');

searchParams.toString() // "q=URLUtils.searchParams&foo=2&foo=3"

URLSearchParams还有三个方法,用来遍历所有参数。

 

keys():遍历所有参数名

values():遍历所有参数值

entries():遍历所有参数的键值对

上面三个方法返回的都是Iterator对象。

 

var searchParams = new URLSearchParams('key1=value1&key2=value2');

 

for (var key of searchParams.keys()) {

  console.log(key);

}

// key1

// key2

 

for (var value of searchParams.values()) {

  console.log(value);

}

// value1

// value2

 

for (var pair of searchParams.entries()) {

  console.log(pair[0]+ ', '+ pair[1]);

}

// key1, value1

// key2, value2

在Chrome浏览器之中,URLSearchParams实例本身就是Iterator对象,与entries方法返回值相同。所以,可以写成下面的样子。

 

for (var p of searchParams) {

  console.log(p);

}

下面是一个替换当前URL的例子。

 

// URL: https://example.com?version=1.0

var params = new URLSearchParams(location.search.slice(1));

params.set('version', 2.0);

 

window.history.replaceState({}, '', `${location.pathname}?${params}`);

// URL: https://example.com?version=2.0

URLSearchParams实例可以当作POST数据发送,所有数据都会URL编码。

 

let params = new URLSearchParams();

params.append('api_key', '1234567890');

 

fetch('https://example.com/api', {

  method: 'POST',

  body: params

}).then(...)

DOM的a元素节点的searchParams属性,就是一个URLSearchParams实例。

 

var a = document.createElement('a');

a.href = 'https://example.com?filter=api';

a.searchParams.get('filter') // "api"

URLSearchParams还可以与URL接口结合使用。

 

var url = new URL(location);

var foo = url.searchParams.get('foo') || 'somedefault';


推荐阅读
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • Redis底层数据结构之压缩列表的介绍及实现原理
    本文介绍了Redis底层数据结构之压缩列表的概念、实现原理以及使用场景。压缩列表是Redis为了节约内存而开发的一种顺序数据结构,由特殊编码的连续内存块组成。文章详细解释了压缩列表的构成和各个属性的含义,以及如何通过指针来计算表尾节点的地址。压缩列表适用于列表键和哈希键中只包含少量小整数值和短字符串的情况。通过使用压缩列表,可以有效减少内存占用,提升Redis的性能。 ... [详细]
  • 在编写业务代码时,常常会遇到复杂的业务逻辑导致代码冗长混乱的情况。为了解决这个问题,可以利用中间件模式来简化代码逻辑。中间件模式可以帮助我们更好地设计架构和代码,提高代码质量。本文介绍了中间件模式的基本概念和用法。 ... [详细]
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • 本文介绍了一道网络流题目hdu4888 Redraw Beautiful Drawings的解题思路。题目要求以行和列作为结点建图,并通过最大流算法判断是否有解以及是否唯一。文章详细介绍了建图和算法的过程,并强调在dfs过程中要进行回溯。 ... [详细]
  • HTML5网页模板怎么加百度统计?
    本文介绍了如何在HTML5网页模板中加入百度统计,并对模板文件、css样式表、js插件库等内容进行了说明。同时还解答了关于HTML5网页模板的使用方法、表单提交、域名和空间的问题,并介绍了如何使用Visual Studio 2010创建HTML5模板。此外,还提到了使用Jquery编写美好的HTML5前端框架模板的方法,以及制作企业HTML5网站模板和支持HTML5的CMS。 ... [详细]
  • Android实战——jsoup实现网络爬虫,糗事百科项目的起步
    本文介绍了Android实战中使用jsoup实现网络爬虫的方法,以糗事百科项目为例。对于初学者来说,数据源的缺乏是做项目的最大烦恼之一。本文讲述了如何使用网络爬虫获取数据,并以糗事百科作为练手项目。同时,提到了使用jsoup需要结合前端基础知识,以及如果学过JS的话可以更轻松地使用该框架。 ... [详细]
  • 从零基础到精通的前台学习路线
    随着互联网的发展,前台开发工程师成为市场上非常抢手的人才。本文介绍了从零基础到精通前台开发的学习路线,包括学习HTML、CSS、JavaScript等基础知识和常用工具的使用。通过循序渐进的学习,可以掌握前台开发的基本技能,并有能力找到一份月薪8000以上的工作。 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • 用Vue实现的Demo商品管理效果图及实现代码
    本文介绍了一个使用Vue实现的Demo商品管理的效果图及实现代码。 ... [详细]
  • 获取ul中第一个li元素的五种方法和多个ul中第一个li元素的四种方法
    本文介绍了获取ul中第一个li元素的五种方法和多个ul中第一个li元素的四种方法,包括使用jQuery的选择器和遍历方法。通过这些方法,可以方便地获取到所需的元素,并进行相应的操作。 ... [详细]
  • 如何压缩网站页面以减少页面加载时间
    本文介绍了影响网站打开时间的两个因素,即网页加载速度和网站页面大小。重点讲解了如何通过压缩网站页面来减少页面加载时间。具体包括图片压缩、Javascript压缩、CSS压缩和HTML压缩等方法,并推荐了相应的压缩工具。此外,还提到了一款Google Chrome插件——网页加载速度分析工具Speed Tracer。 ... [详细]
  • loader资源模块加载器webpack资源模块加载webpack内部(内部loader)默认只会处理javascript文件,也就是说它会把打包过程中所有遇到的 ... [详细]
author-avatar
qinsg688_377
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有