AJAX技术:历史纪录的保存 - 摘自《程序员06第12期》作者:陈英

概述:
      AJAX 技术作为Web2.0的标志技术,其良好的用户体验、优良的设计模式、众多浏览器厂家的广泛支持,正日益受到人们的关注和学习。
      从本质上说AJAX(Async-hronous JavaScript And XHTML)并不是新技术,它描述了JavaScript、DOM、CSS、XHTML等一组技术,使浏览器可以为用户提供更为自然的浏览体验。
      在AJAX之前,Web站点强制用户进入提交/等待/重新显示循环,用户的动作总是与服务器的响应时间同步。
      AJAX提供与服务器异步通信的能力,从而使用户从请求/ 响应的循环中解脱出来。借助于AJAX,可以在用户单击按钮时,使用JavaScript 和DHTML 立即更新UI,并向服务器发出异步请求,以执行服务器后端操作。当请求返回时,就可以使用JavaScript 和CSS 来相应地更新UI,而不是刷新整个页面。
      最重要的是,用户甚至不知道浏览器正在与服务器通信:Web 站点看起来是即时响应的。例如:gmail 中邮件撰写过程中邮件草稿的自动保存;国内优秀的Web2.0 网站抓虾( www.zhuaxia.com)几乎全站采用AJAX 交互方式.


问题:
      但正由于AJAX的异步特征,使得历史记录的保存上存在问题。
      目前的浏览器历史记录是基于传统同步请求响应的,不同的URL 对应不同的页面每次页面操作在URL变化的同时整个页面也产生改变;而AJAX异步请求异步响应,页面操作往往只刷新部分页面UI,同一个URL 可能对应于不同的请求和页面响应,在浏览器前进、后退及刷新等浏览器历史操作中,浏览器不能根据URL 准确地发出历史页面的请求,用户也就不能得到正确的页面响应,影响用户体验。

解决方案:
      笔者在学习、使用AJAX的基础上,找到了解决这一问题的方法,其基本思想是: 不依赖于浏览器而通过编码记录历史页面请求;在浏览器历史浏览操作中,先找到历史页面请求,再重新执行这些请求来达到正确浏览历史页面的作用。
      其具体实现方案有以下两种:
       1。函数法
      采用这函数法有一个前提,需要页面是通过某个JavaScript 函数的执行而得到。在这样的前提下,把该函数名作为URL 的一部分放在地址栏中,就可以通过URL 记录下当前页面的执行函数,这样在浏览器前进、后退或刷新等操作时,先获得当前URL 找到相应的执行函数,然后重新执行该函数,即可获得正确的页面结果。对每一个改变页面显示的JavaScript函数,需要在执行其业务逻辑之前,更改当前页面的URL,即将函数名及参数作为URL的Hash部分。例如,函数showPageInfo以页面显示条目数及页码为参数显示某一页的内容。则在其业务逻辑之前,需要修改页面URL。
ExpandedBlockStart.gif ContractedBlock.giffunction showPageInfo( page_size , page_num ) dot.gif{
InBlock.gif// 参数检查等
InBlock.gif dot.gif
InBlock.gif//function_info 是一个字符串,由函数名及相应参数组成的字符串
InBlock.gifvar function_info = “showPageInfo(page_size , page_num )”;
InBlock.gifchangeURLLocation(function_info);
InBlock.gif// 业务逻辑
InBlock.gif dot.gif
ExpandedBlockEnd.gif}
而函数changeURLLocation修改页面URL,将函数信息作为URL 的Hash部分。
ExpandedBlockStart.gif ContractedBlock.giffunction changeURLLocation( function_info ) dot.gif{
InBlock.gif//function_info 是一个字符串,由函数名及相应参数组成的字符串
InBlock.gif// 将函数信息作为新URL 的Hash 部分
InBlock.gifVar current_url = “http://somesite/#” + function_info;
InBlock.gif// 将新URL 写入地址栏
InBlock.gifwindow.location = current_url;
InBlock.gif dot.gif
ExpandedBlockEnd.gif}
同时,需要函数LocationChange(),当地址栏URL 发生变化时,获得当前URL 中的函数名及参数,并通过eval方法执行之。
ExpandedBlockStart.gif ContractedBlock.giffunction LocationChange() dot.gif{
InBlock.gif// 第一步,获得当前页面URL 中的函数名
ExpandedSubBlockStart.gif ContractedSubBlock.gif/**//* 注意,请不要使用window.location.hash,因为在笔者的使用过程中发现此种方法*/
InBlock.gif// 可能出现错误
InBlock.gif  var currentLocation = window.location.href;
InBlock.gif  var function_info = “”;
InBlock.gif  if( currentLocation && currentLocation.indexOf(“#”) > -1)
ExpandedSubBlockStart.gif ContractedSubBlock.gif   dot.gif{
InBlock.gif    var index = currentLocation.indexOf(“#”);
InBlock.gif    if (currentLocation.length > (index+1))
ExpandedSubBlockStart.gif ContractedSubBlock.gif     dot.gif{
InBlock.gif      function_info = currentLocation.substring(index+1);
ExpandedSubBlockEnd.gif    }
ExpandedSubBlockEnd.gif  }
InBlock.gif
InBlock.gif  // 第二步,重执行函数
InBlock.gif  if (function_info == “”)
ExpandedSubBlockStart.gif ContractedSubBlock.gif   dot.gif{
InBlock.gif    // 为空时默认执行某函数或做其他处理
InBlock.gif     dot.gif
ExpandedSubBlockEnd.gif  }
InBlock.gif  else
ExpandedSubBlockStart.gif ContractedSubBlock.gif   dot.gif{
InBlock.gif    // 执行函数
InBlock.gif    try
ExpandedSubBlockStart.gif ContractedSubBlock.gif     dot.gif{
InBlock.gif      eval(function_info);
ExpandedSubBlockEnd.gif    }
InBlock.gif    catch(error)
ExpandedSubBlockStart.gif ContractedSubBlock.gif     dot.gif{
InBlock.gif       dot.gif
ExpandedSubBlockEnd.gif    }
ExpandedSubBlockEnd.gif  }
ExpandedBlockEnd.gif}
   这样, 当浏览器前进、后退或者刷新操作触发时,LocationChange()函数将能够获得当前页面的执行函数并执行之,也就能获得正确的页面响应了。但为了区分不同的页面,几乎每个页面操作都需要在URL 上有所体现,即需要修改对应函数的某些参数,这就增加了程序的复杂度。

       2。URL 映射法
      URL 映射法的提出是基于对AJAX 本质的这样一种认识: 
      AJAX 请求的本质是请求服务器资源的URL,以及执行AJAX响应的回调函数(如果是POST方法,还应包括POST 的内容;如果是同步请求,还应包含同步请求的标志,这里一般认为是异步请求)。因此,只要记录下AJAX 请求的URL 及其回调函数,就可以重新执行该AJAX 请求。
      对不同页面,以地址栏URL 为标识,记录下其对应的各个AJAX请求URL 和回调函数。在浏览器前进、后退或刷新等操作时,先通过当前地址栏URL 获得对应的各个AJAX请求URL和回调函数,然后重新发出这些AJAX 请求,即可获得正确的页面结果。
      函数sendAJAXRequest通过请求URL及回调函数callback发出AJAX 异步请求。
      
None.gif function sendAJAXRequest( request_url , callback )
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
// 通过request_url 发出AJAX 异步请求,并通过callback执行响应
InBlock.gif
  dot.gif
ExpandedBlockEnd.gif}
      对象historyStorage以键值对的形式存储每个地址栏URL 对应的各个AJAX 请求信息。一般来说,地址栏URL 为键,而由于其对应AJAX请求可能多于一个,故值为包含各个AJAX请求信息二维数组。
None.gif window.historyStorage  =   new  Object();
      如某页面地址栏URL 为 http://somesite/#showPageInfo(10,0),对应的AJAX 异步请求的URL 为request_url,回调函数为showPageInfo_Callback()。则存储在historyStorage中的形式为:
None.gif historyStorage[‘http: // somesite/#showPageInfo(10,0)’] =
None.gif
[[request_url, showPageInfo_Callback]];
      这样,函数showPageInfo()的实现应改为:
ExpandedBlockStart.gif ContractedBlock.gif function showPageInfo( page_size , page_num ) dot.gif {
InBlock.gif
// 参数检查等
InBlock.gif
dot.gif
InBlock.gif
//current_url 是地址栏URL,同时也是historyStorage 的键
InBlock.gif
var current_url = “http://somesite/#showPageInfo
InBlock.gif
(page_size , page_num )”;
InBlock.gif
//AJAX 请求的URL
InBlock.gif
var request_url = “SomeResource”;
InBlock.gif
//AJAX 请求的回调函数,处理异步响应
ExpandedSubBlockStart.gifContractedSubBlock.gif
var showPageInfo_Callback = function( response )dot.gif{
InBlock.gif
// 回调函数逻辑
InBlock.gif
dot.gif
ExpandedSubBlockEnd.gif}

InBlock.gifdot.gif
InBlock.gif
// ajax_info_array 用于存放各个AJAX 请求信息
InBlock.gif
var ajax_info_array = new Array();
InBlock.gifajax_info_array[
0= [request_url, showPageInfo_Callback];
InBlock.gifsaveHistory(current_url, ajax_info_array);
InBlock.gif
// 业务逻辑
InBlock.gif
dot.gif
ExpandedBlockEnd.gif}
      其中,函数saveHistory() 将地址栏URL 及其对应的各个AJAX 请求信息存储在historyStorage中:
ExpandedBlockStart.gif ContractedBlock.gif function saveHistory( url , ajax_info_array ) dot.gif {
InBlock.gif
// 参数检查
InBlock.gif
dot.gif
InBlock.gif
// 存储在historyStorage 中
InBlock.gif
historyStorage[url] = ajax_info_array;
InBlock.gifdot.gif
ExpandedBlockEnd.gif}

None.gif
      与其对应的,函数getHistory()通过当前地址栏URL 得到包含各个对应AJAX 请求信息的二维数组:
ExpandedBlockStart.gif ContractedBlock.gif function getHistory( url ) dot.gif {
InBlock.gif
// 如果historyStorage中没有,返回空
ExpandedSubBlockStart.gifContractedSubBlock.gif
if ( typeof historyStorage[url] == “undefined” )dot.gif{
InBlock.gif
return null;
ExpandedSubBlockEnd.gif}

InBlock.gif
// 返回当前URL 对应的各个AJAX 请求,二维数组
InBlock.gif
return historyStorage[url];
ExpandedBlockEnd.gif}
      同样,需要函数LocationChange(),但此时函数实现改为:
ExpandedBlockStart.gif ContractedBlock.gif function LocationChange() dot.gif {
InBlock.gif
// 第一步,获得当前页面URL 中的函数名
InBlock.gif
var currentLocation = window.location.href;
InBlock.gif
// 第二步,得到对应的各个AJAX 请求信息
InBlock.gif
var ajax_info_array = getHistory(currentLocation);
ExpandedSubBlockStart.gifContractedSubBlock.gif
if (ajax_info_array == null )dot.gif{
InBlock.gif
return;
ExpandedSubBlockEnd.gif}

InBlock.gif
// 第三步,重发各个AJAX 请求
ExpandedSubBlockStart.gifContractedSubBlock.gif
for (var i = 0 ; i < ajax_info_array.length ; i++)dot.gif{
InBlock.gif
// 遍历每个对应的AJAX请求信息,重新发出各个AJAX请求
InBlock.gif
sendAJAXRequest( ajax_info_array[i][0]
InBlock.gif, ajax_info_array[i][
1] );
ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

None.gif
      这样,当浏览器前进、后退或者刷新操作触发时,L o c a t i o n C h a n g e ( ) 函数先获得当前页面的URL ,然后从historyStorage中找到对应的各个AJAX信息并依次重新发出,就能获得正确的页面响应。这种方法不需要地址栏中给出函数名,但各种信息存储在内存中,是一个占用。

以上两种方法,都暂时地解决了AJAX 历史记录问题,但各有利弊,应据需要应变。当然,解决问题的方法多种多样,欢迎探讨。笔者相信,随着AJAX 的发展,各种新问题、新办法都将出现,但只要抓住问题的本质都能够顺利解决。


 -- 摘自《程序员06第12期》作者:陈英

转载于:https://www.cnblogs.com/tarzan/archive/2007/08/01/839073.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值