Tarzan

我对将来没有安全感。我没能力把握将来发生的一切事情,所以我只能选择自己有把握的东西。

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
概述:
      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。
function showPageInfo( page_size , page_num ){
// 参数检查等

//function_info 是一个字符串,由函数名及相应参数组成的字符串
var function_info = “showPageInfo(page_size , page_num )”;
changeURLLocation(function_info);
// 业务逻辑

}
而函数changeURLLocation修改页面URL,将函数信息作为URL 的Hash部分。
function changeURLLocation( function_info ){
//function_info 是一个字符串,由函数名及相应参数组成的字符串
// 将函数信息作为新URL 的Hash 部分
Var current_url = “http://somesite/#” + function_info;
// 将新URL 写入地址栏
window.location = current_url;

}
同时,需要函数LocationChange(),当地址栏URL 发生变化时,获得当前URL 中的函数名及参数,并通过eval方法执行之。
function LocationChange(){
// 第一步,获得当前页面URL 中的函数名
/**//* 注意,请不要使用window.location.hash,因为在笔者的使用过程中发现此种方法*/
// 可能出现错误
  var currentLocation = window.location.href;
  var function_info = “”;
  if( currentLocation && currentLocation.indexOf(“#”) > -1)
  {
    var index = currentLocation.indexOf(“#”);
    if (currentLocation.length > (index+1))
    {
      function_info = currentLocation.substring(index+1);
    }
  }

  // 第二步,重执行函数
  if (function_info == “”)
  {
    // 为空时默认执行某函数或做其他处理
    
  }
  else
  {
    // 执行函数
    try
    {
      eval(function_info);
    }
    catch(error)
    {
      
    }
  }
}
   这样, 当浏览器前进、后退或者刷新操作触发时,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 异步请求。
      
function sendAJAXRequest( request_url , callback )
{
  
// 通过request_url 发出AJAX 异步请求,并通过callback执行响应
  
}
      对象historyStorage以键值对的形式存储每个地址栏URL 对应的各个AJAX 请求信息。一般来说,地址栏URL 为键,而由于其对应AJAX请求可能多于一个,故值为包含各个AJAX请求信息二维数组。
window.historyStorage = new Object();
      如某页面地址栏URL 为http://somesite/#showPageInfo(10,0),对应的AJAX 异步请求的URL 为request_url,回调函数为showPageInfo_Callback()。则存储在historyStorage中的形式为:
historyStorage[‘http://somesite/#showPageInfo(10,0)’] =
[[request_url, showPageInfo_Callback]];
      这样,函数showPageInfo()的实现应改为:
function showPageInfo( page_size , page_num ){
// 参数检查等

//current_url 是地址栏URL,同时也是historyStorage 的键
var current_url = “http://somesite/#showPageInfo
(page_size , page_num )”;
//AJAX 请求的URL
var request_url = “SomeResource”;
//AJAX 请求的回调函数,处理异步响应
var showPageInfo_Callback = function( response ){
// 回调函数逻辑

}


// ajax_info_array 用于存放各个AJAX 请求信息
var ajax_info_array = new Array();
ajax_info_array[
0= [request_url, showPageInfo_Callback];
saveHistory(current_url, ajax_info_array);
// 业务逻辑

}
      其中,函数saveHistory() 将地址栏URL 及其对应的各个AJAX 请求信息存储在historyStorage中:
function saveHistory( url , ajax_info_array ){
// 参数检查

// 存储在historyStorage 中
historyStorage[url] = ajax_info_array;

}

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

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

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

}

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

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


 -- 摘自《程序员06第12期》作者:陈英
posted on 2007-08-01 15:18  泰山  阅读(434)  评论(0编辑  收藏  举报