Ajax部分页面刷新和History API连系的圈套

ajax在当代网站已取得异常普各处运用,重要的优点人人都晓得(异步加载数据,不必革新全部浏览器,更小的数据传输尺寸)。关于那些老网站或许老项目来讲通盘改形成ajax并不实际,因而就有了“部分页面革新”这个处理计划。假如不晓得“部分页面革新”是何物请看这里这里这里

在我们的项目里,将本来的iframe或许frame统统替代成了时兴的div,然后修正了页面上统统提议要求的处所,把相应内容jQuery.loaddiv里。

因而乎本来老旧的网站变成了一个时兴的基于ajax的网站,每一个页面传输的数据量变小了,再也不必处理使人头疼的:

  1. 为了消弭滚动条让iframe自适应大小

  2. 怎样接见parent window变量的题目(另有怎样接见parent的parent的parent… window变量的题目)

  3. 怎样接见child iframe里的变量[]的题目了(另有怎样接见child的child的child… iframe里的变量的题目)

由于人人永久都在同一个window里,而且div自身就会依据内容自动撑大。然则等等!浏览器怎样不能退却了?

我们的谁人项目是一个满大街可见的XX管理信息体系,这类体系最常见的计划就是左边一个树形菜单地区,右边是一个功用地区,功用地区里有一个查询前提地区(内里有个查询按钮),另有一个空缺的地区用来显现查询效果,同时是用户操纵数据的处所(比方form表单)。

在iframe时期,上面讲到的4个地区都是一个iframe,这也就意味着我们可以有很变态的退却才能。
固然了一般来讲用户最经常运用的就是对操纵地区做退却行动,比方查询一下,挑选一条纪录点击修正,看到form表单,修正一下,在点击保留前忏悔了,点击浏览器的退却,回到查询效果页面。

然则在引入了ajax后没法退却了,由于ajax要求不会纪录到浏览器汗青里,汗青都没有了天然就没法退却了。

幸亏Html5的History API可以协助我们处理题目。我们可以工资的运用history.pushState来人造汗青信息,而且经由过程监听popstate事宜来晓得用户点击了浏览器退却或行进按钮,然后将页面元素复原到汗青上的某个状况。关于Html5 History API的相干信息可以看这里

然则事变远不止这么简朴,下面是我们碰到的一些坑:

圈套1:反复实行js剧本

// 点击查询按钮的时刻工资组织一个浏览器汗青
$('#some-button').click(function() {
  $(targetSelector).load(url);

  history.pushState({
    container : targetSelector,
    content   : $(targetSelector).html()
  }, null, url);

});

// 当浏览器退却后者行进的时刻,我们把当时的效果从新加载到container里来
window.addEventListener('popstate', function() {
  var state = history.state
  $(state.container).html(state.content);
})

统统看上去都OK,直到…我们发明部分页面革新所取得的效果里包含了操纵dom元素的js。

当碰到这类状况时会发作很巧妙的征象,history state.content是已加载终了+js实行后的效果,当我们从新复原的时刻,我们会把这个效果加载出来,而且又会实行一遍js。假如这个js是一个增加dom的行动那末在退却的时刻你会看到这个反复的dom元素。

我们想过跟踪哪些dom元素是被js修正过的来防止这个题目,然则…这是不实际的。

圈套2:没法复原到最初状况

前面的计划由于load的内容里能够有js剧本所以有严峻缺点,因而我们换了个思绪,history里保留responseText,而不是已load好后的东西。

// 点击查询按钮的时刻工资组织一个浏览器汗青
$('#some-button').click(function() {
  $(targetSelector).load(url, function(responseText) {
    history.pushState({
      container : targetSelector,
      content   : responseText
    }, null, url);
  });
});

// popstate事宜的处理方式一样

然则依然碰到了这么一个题目,假如container(革新目的地区,某个div)本来是有内容的,而这个内容不是经由过程ajax部分页面革新而来,而是用户一进入这个页面就已有的,比方运用服务器端的模板引擎天生的页面,那末在它加载完html片断后就没法回退了。由于它的内容一开始就不在history里(事实上浏览器本身发生的history是没有state的),如许就形成了退无可退的局势。

假如你想,我们只需保留这个container本来的内容不就好了,当退却的时刻我们直接恢复它本来的内容,然则请看圈套1

不过当发作退无可退的状况时,我们认为已退回到了第一次进入页面的状况,这个时刻我们革新全部页面就好了。

圈套3:多个并列的container

圈套2的处理计划实际上是基于container之间是属于嵌套关联或许就一个container的状况的。假如是这类状况就不行了:

有A和B两个container,点击某个按钮革新了A的内容(发生汗青),然后在点击某个按钮革新的B的按钮(发生汗青),根据用户的料想状况,第一次退却复原B本来的内容,第二次退却复原A本来的内容。但实际上,第一次退却没法复原B的内容(圈套2),第二次退却页面革新了(统统恢复最初的模样)。

假如B是嵌套在A里的就无所谓了,第一次退却的时刻取得的是A的state,依据A的state复原A的内容的时刻顺便把B也复原了,第二次退却页面革新,把A也复原了。

而且依据圈套1所讲,我们也不能在history里存储A或许B里本来的内容。

处理办法:关于这类操纵就不要纪录汗青了。

圈套4:看到过期页面

我们在History state里存的是当时load时的responseText,当我们退却的时刻看到的是过期的页面,比方我们本来查询效果里看到有A纪录,然后我们跳转到其他页面里,然后再退却到查询效果页面看到A纪录还在,然则这个A纪录极能够只是一个幽魂,在数据库里早就已不存在了。假如我们这个时刻再对A纪录操纵就有涌现毛病。

处理办法是我们在history state里保留url已相干的参数,当popstate的时刻从新提议要求就好了,如许一来的话也减少了history存储state所须要的空间。

// 这里只给get要求的例子,post的道理也差不多
$('#some-button').click(function() {
  $(targetSelector).load(url, function(responseText) {
    history.pushState({
      container : targetSelector,
      url       : url
    }, null, url);
  });
});

window.addEventListener('popstate', function() {
  var state = history.state;
  $(state.container).load(state.url);
});

圈套5:redirect

纵然我们在history state保留了url你就认为没事了?too simple, too naive!假如我们对这个url提议的要求被服务器redirect到另一个url,那末在history state里保留这个url就不对了。

假如我们这个url是用来删除某条纪录的,服务器收到要求在数据库里删除了这条纪录,然后redirect到了首页url,那末这个时刻你在history里应当存谁人url呢?显然是首页的url,由于假如你存了删除url,那末在退却的时刻,我们会从新提议这个url,想一想这多吓人。

处理办法实在不太简朴,由于ajax是不是被redirect你是不晓得的,用jQuery封装的jqXHR对象也没法晓得这个。

或许链WHATWG的XmlHttpRequest.responseURL可以救你,然则浏览器兼容性不好。

我的做法在服务器sendRedirect之前在requestUrlqueryString里增加一个flag,用一个特地的servlet filter推断过来的要求是不是有这个flag,假如有那末就将本次要求的url(也就是redirect到的url)放到response的一个特定的header里。然后就可以用jqXHR.getResponseHeader('some-header')来取得这个url,把这个url放到history state里。

圈套6:没法准确复原dom对象的状况

不论是保留responseText照样保留url要求参数,都没法在浏览器退却的时刻准确复原dom对象的状况,比方我在IE6里有个如许的特征,你在某个页面勾选了某个checkbox,然后跳转到一个新的页面然后再退却,谁人checkbox照样处于勾选状况,这个在应用ajax部分页面革新里是完整做不到的,想到用户和我说之前退却的时刻谁人勾还在如今勾没有了,不处理这个BUG就不验收的事变时才想到iframe的好啊。

所以假如要准确复原dom对象的状况,得在history.pushState的时刻自行把相干信息保留下来,在popstate的时刻用到这些信息并复原dom。

事实上即运用了iframe也并非统统的浏览器会复原dom对象状况,看这篇文章

总结

  1. 不要随意马虎从iframe切换到ajax部分页面革新

  2. 要本身掌握那些ajax部分页面革新纪录汗青,哪些不纪录,有些时刻能够还须要replaceState,不要想固然的把统统要求都纪录汗青

  3. 把代码改形成ajax部分页面革新只是第一步,还须要对全部网站、运用的UI做计划和设想,关于这个题目不存在通用的处理计划

参考资料

    原文作者:chanjarster
    原文地址: https://segmentfault.com/a/1190000006240654
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞