几年前,Ajax的兴起给互联网带来了新的生机,同时也使用户体验有了质的飞跃,用户无需刷新页面便可获取新的数据,而页面也以一种更具备交互性的形式为用户展示视图,能够说这种变化对互联网发展的贡献是前所未有的。javascript
但随着Ajax大规模应用,愈来愈多的开发人员开始注意到其中存在的问题,由于Ajax的视图展示是在页面无刷新状况下进行的,这也就意味着在用户作了一系列操做以后,页面的URL是没有任何变化的,这些操做所产生的结果天然也就没法保留,当用户再次访问时,想要展示的数据也就没法重现。css
举个例如来讲,咱们在一个摄影网站浏览一个相册,点击第一张图片会动态弹出一个大图的相框,能够更好地浏览图片,当看到一个本身喜欢的图片时,急于分享这张图片,因而把当前的URL发布出去了,可是咱们的好友点击进去后发现,看到的只是原来的那个相册,并无弹出的大图相框,更别说咱们要分享的那张图片了,是否是很失望。也正是由于如此,这个页面没法被搜索引擎精确地抓取,SEO没法作到优化,用户的可访问性也大打折扣。html
为了解决这个问题,开发者开始尝试使用URL中的hash来提升可访问性(hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)),部分浏览器开始提供hash相关的事件支持,有些知名站点也和搜索引擎约定使用某种规则来对站点页面进行抓取,但这毕竟不是一个标准的技术,仍是不可避免的存在不少问题,开发者也期待出现一个标准化的完美解决方案,完全解决这个难题。java
幸运的是,HTML5中的History API给开发者带了新的但愿,它很好的支持了基于URL的页面无刷新操做,也使得SEO优化获得完美的解决,毫无疑问,History API将会成为Web领域将来的标准,愈来愈多的开发者也将会使用它来开发本身的应用程序。jquery
那么今天,咱们就来说解一下与History相关的技术。浏览器
为了可以更好的阐述这项新技术,咱们选择使用一个示例程序来开始。以下图所示,咱们展现三张小图,点击任意一张,将会弹出大的预览图,而后能够先后切换预览图片:框架
这个程序对于你们来讲应该说是比较简单的,当点击小图时,将放置大图的页面元素显示出来,图片和简介显示出与小图对应的信息便可,切换预览图片的操做也是很是容易作到的。ide
这就是传统的Ajax操做,而这种操做存在一个极其严重的缺陷,咱们没法记录当前浏览的图片信息,假如后面的小图有几百张,当咱们浏览到某一张时,以为很是不错,想把它发送给朋友,也有可能想本身存下来,这时候咱们会发现,URL中没有任何这张图片的信息,复制地址栏也就没有任何效果了。函数
因此咱们极为迫切的须要在URL中加入用户操做所产生的信息,这个时候History API就派上用场了,他提供的方法能够在页面不刷新的状况下对URL进行更新操做。下面就介绍一下主要的两个方法:优化
history.pushState(stateObj, title, url);
这个方法会往当前会话的历史栈中放入一条记录,stateObj是用户自定义的对象,用于记录一些有用的数据,title顾名思义就是标题信息,第三个参数是要放入的url信息。例如咱们点击第二张图片时能够执行history.pushState({id: 2}, 'img: 2', '?preview=2');
history.replaceState(stateObj, title, url);
这个方法于pushState相似,惟一不一样的是,执行这个操做后,浏览器会对会话历史栈内的记录进行替换而不是添加,这在特定场景下是比较适用的。
咱们这里就来结合实例来说解一下如何操做历史记录。
正如上图看到的那样,页面初始化时展示三张小图,点击任意一张小图时,会弹出预览图,页面的基本框架以下:
而后页面初始化时,咱们须要为一些DOM元素绑定事件,就像下面这样:
能够看到几个绑定的事件,点击小图会触发预览模式;而后在预览时点击关闭按钮,会隐藏预览框,同时调用了history的back函数,咱们稍后会介绍;最后是两个切换按钮的事件,分别会切换到上一张或下一张大图。咱们注意到,当小图被点击时,调用了一个叫preview的函数,这个函数正是咱们重点要讲的,代码以下:
上面的preview函数主要负责几个重要的工做,首先显示预览框,而后根据数据ID获取数据信息,最后操做历史记录对象。在实际开发中,getPreviewDataById函数大可能是以Ajax方式进行的,这个时候只需将后面的逻辑放到回调函数内部便可。在preview函数的最后,咱们往历史记录里面放入或替换一条包含ID信息的对象,同时改变URL,放入仍是替换,取决因而不是先后切换预览图,咱们稍后会详细解释一下。注意,这个时候虽然URL改变了,但页面并不会刷新。下面咱们就来对比一下点击先后的效果,以点击第二张小图为例:
能够明显看到,地址栏已经变化了,这其实就是咱们调用pushState函数的结果:
window.history.pushState({id: 2}, 'img: 2', '?preview=2');
须要额外注意的一点是,第三个参数是新的URL地址,根据官方文档介绍,它能够是相对地址,也能够是绝对地址,因此咱们也能够选择使用绝对地址,就像下面这样,效果是同样的:
window.history.pushState({id: 2}, 'img: 2','http://localhost/history/?preview=2');
在上面的preview函数参数列表中,存在一个isSwitch参数,若是函数被调用时指定这个参数,则会调用replaceState,而不是pushState,这是为何呢,下面就来解释一下。
还记得咱们咱们为预览框右上角关闭按钮绑定的事件吗,除了隐藏预览图,还调用了history的back函数,其实咱们是要利用这个函数从历史栈中弹出以前放入的历史记录,进而将URL恢复到预览前的状态。那么,若是咱们在预览的时候频繁的切换到下一张或者上一张,历史记录栈中就会存在不少记录,要回到初始状态显然不易,咱们也没有必要保留这么多的历史记录,因此当用户先后切换预览图时,只须要调用replaceState替换当前历史栈中那条记录便可,假如咱们点击右边的切换按钮,应该是这样的:
为了调用replaceState函数,咱们须要在执行preview函数时,传入一个isSwitch参数,咱们来看看是如何调用的:
下面就以两个示意图来说解一下切换先后的栈结构:
这样咱们就能保证历史栈中只有一条记录,当关闭预览框结束预览时,可当即恢复到以前的视图。
而向前切换也是同样的,代码以下:
到这里,我想你们都已经清楚如何操做历史栈来改变URL,那么咱们不只要问,若是使用当前的URL从新访问站点,如何还原关闭以前的视图呢,其实这个就属于基本的操做了,只须要在onload函数时获得相应的参数,而后调用preview函数便可,如代码所示:
//从URL中获取到当前预览图片的ID function getCurrPreviewId() { var search = location.search; if (!search) return -1; var params = {}; var keyValues = search.substring(1).split('&'); keyValues.forEach(function(item) { var parts = item.split('='); params[parts[0]] = parts[1]; }); if (!params['preview']) return -1; return params['preview']; } //当页面加载时,若是URL中有预览信息,取出而后 window.onload = function() { var id = getCurrPreviewId(); if (id < 1 || id > 3) return; preview(id); };
到这里其实咱们已经将整个过程讲解完成了,但还有一个重要的事件咱们尚未涉及到,那就是window下面的onpopstate事件,当用户点击后退按钮或前进按钮时,或者当咱们的程序执行history.back(),history.forward(),history.go()时,都会触发这个事件,它可以捕获这些操做完成以后,当前历史栈中的状态,咱们能够利用这些信息,作进一步的操做。那么咱们就来作个试验,看看onpopstate事件是如何被触发的:
在这个事件函数内,咱们会打印事件对象中的state对象,其实就是咱们执行pushState函数时放进去的对象,下面咱们在控制台作下面几个操做看看效果:
如图所示,咱们放入两条记录,当后退或前进的时候,都会执行到onpopstate事件函数,而且可以获取到当前的状态对象信息,若是咱们的图片预览功能须要记录每一步的操做,那么咱们就能够在onpopstate事件函数中获取到当前须要展示的图片ID,而后直接调用preview函数便可。
最后,须要注意的是,当前有些浏览器还不能很好的支持History API,因此咱们最好判断一下浏览器的支持状况:
讲到这里,关于History的相关知识及应用基本上就告一段落了,但愿你们可以细细体会它的应用场景,并加以练习,最后但愿你们可以很好地掌握并运用到将来的项目中去。