概述:
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 showPageInfo( page_size , page_num ) {
{
 // 参数检查等
// 参数检查等


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


 }
而函数changeURLLocation修改页面URL,将函数信息作为URL 的Hash部分。
}
而函数changeURLLocation修改页面URL,将函数信息作为URL 的Hash部分。

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


 }
同时,需要函数LocationChange(),当地址栏URL 发生变化时,获得当前URL 中的函数名及参数,并通过eval方法执行之。
}
同时,需要函数LocationChange(),当地址栏URL 发生变化时,获得当前URL 中的函数名及参数,并通过eval方法执行之。

 function LocationChange()
function LocationChange() {
{
 // 第一步,获得当前页面URL 中的函数名
// 第一步,获得当前页面URL 中的函数名

 /**//* 注意,请不要使用window.location.hash,因为在笔者的使用过程中发现此种方法*/
/**//* 注意,请不要使用window.location.hash,因为在笔者的使用过程中发现此种方法*/
 // 可能出现错误
// 可能出现错误
 var currentLocation = window.location.href;
  var currentLocation = window.location.href;
 var function_info = “”;
  var function_info = “”;
 if( currentLocation && currentLocation.indexOf(“#”) > -1)
  if( currentLocation && currentLocation.indexOf(“#”) > -1)

 
   {
{
 var index = currentLocation.indexOf(“#”);
    var index = currentLocation.indexOf(“#”);
 if (currentLocation.length > (index+1))
    if (currentLocation.length > (index+1))

 
     {
{
 function_info = currentLocation.substring(index+1);
      function_info = currentLocation.substring(index+1);
 }
    }
 }
  }

 // 第二步,重执行函数
  // 第二步,重执行函数
 if (function_info == “”)
  if (function_info == “”)

 
   {
{
 // 为空时默认执行某函数或做其他处理
    // 为空时默认执行某函数或做其他处理
 
    
 }
  }
 else
  else

 
   {
{
 // 执行函数
    // 执行函数
 try
    try

 
     {
{
 eval(function_info);
      eval(function_info);
 }
    }
 catch(error)
    catch(error)

 
     {
{
 
      
 }
    }
 }
  }
 }
   这样, 当浏览器前进、后退或者刷新操作触发时,LocationChange()函数将能够获得当前页面的执行函数并执行之,也就能获得正确的页面响应了。但为了区分不同的页面,几乎每个页面操作都需要在URL 上有所体现,即需要修改对应函数的某些参数,这就增加了程序的复杂度。
}
   这样, 当浏览器前进、后退或者刷新操作触发时,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 )
function sendAJAXRequest( request_url , callback )
 {
{
 // 通过request_url 发出AJAX 异步请求,并通过callback执行响应
  // 通过request_url 发出AJAX 异步请求,并通过callback执行响应
 
  
 }
      对象historyStorage以键值对的形式存储每个地址栏URL 对应的各个AJAX 请求信息。一般来说,地址栏URL 为键,而由于其对应AJAX请求可能多于一个,故值为包含各个AJAX请求信息二维数组。
}
      对象historyStorage以键值对的形式存储每个地址栏URL 对应的各个AJAX 请求信息。一般来说,地址栏URL 为键,而由于其对应AJAX请求可能多于一个,故值为包含各个AJAX请求信息二维数组。
 window.historyStorage = new Object();
      如某页面地址栏URL 为http://somesite/#showPageInfo(10,0),对应的AJAX 异步请求的URL 为request_url,回调函数为showPageInfo_Callback()。则存储在historyStorage中的形式为:
window.historyStorage = new Object();
      如某页面地址栏URL 为http://somesite/#showPageInfo(10,0),对应的AJAX 异步请求的URL 为request_url,回调函数为showPageInfo_Callback()。则存储在historyStorage中的形式为:
 historyStorage[‘http://somesite/#showPageInfo(10,0)’] =
historyStorage[‘http://somesite/#showPageInfo(10,0)’] =
 [[request_url, showPageInfo_Callback]];
      这样,函数showPageInfo()的实现应改为:
[[request_url, showPageInfo_Callback]];
      这样,函数showPageInfo()的实现应改为:
 function showPageInfo( page_size , page_num ){
function showPageInfo( page_size , page_num ){
 // 参数检查等
// 参数检查等


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


 }
}


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


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


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


 }
}
 与其对应的,函数getHistory()通过当前地址栏URL 得到包含各个对应AJAX 请求信息的二维数组:
      与其对应的,函数getHistory()通过当前地址栏URL 得到包含各个对应AJAX 请求信息的二维数组:
 function getHistory( url ){
function getHistory( url ){
 // 如果historyStorage中没有,返回空
// 如果historyStorage中没有,返回空
 if ( typeof historyStorage[url] == “undefined” ){
if ( typeof historyStorage[url] == “undefined” ){
 return null;
return null;
 }
}
 // 返回当前URL 对应的各个AJAX 请求,二维数组
// 返回当前URL 对应的各个AJAX 请求,二维数组
 return historyStorage[url];
return historyStorage[url];
 }
      同样,需要函数LocationChange(),但此时函数实现改为:
}
      同样,需要函数LocationChange(),但此时函数实现改为:
 function LocationChange(){
function LocationChange(){
 // 第一步,获得当前页面URL 中的函数名
// 第一步,获得当前页面URL 中的函数名
 var currentLocation = window.location.href;
var currentLocation = window.location.href;
 // 第二步,得到对应的各个AJAX 请求信息
// 第二步,得到对应的各个AJAX 请求信息
 var ajax_info_array = getHistory(currentLocation);
var ajax_info_array = getHistory(currentLocation);
 if (ajax_info_array == null ){
if (ajax_info_array == null ){
 return;
return;
 }
}
 // 第三步,重发各个AJAX 请求
// 第三步,重发各个AJAX 请求
 for (var i = 0 ; i < ajax_info_array.length ; i++){
for (var i = 0 ; i < ajax_info_array.length ; i++){
 // 遍历每个对应的AJAX请求信息,重新发出各个AJAX请求
// 遍历每个对应的AJAX请求信息,重新发出各个AJAX请求
 sendAJAXRequest( ajax_info_array[i][0]
sendAJAXRequest( ajax_info_array[i][0]
 , ajax_info_array[i][1] );
, ajax_info_array[i][1] );
 }
}
 }
}
 这样,当浏览器前进、后退或者刷新操作触发时,L o c a t i o n C h a n g e ( ) 函数先获得当前页面的URL ,然后从historyStorage中找到对应的各个AJAX信息并依次重新发出,就能获得正确的页面响应。这种方法不需要地址栏中给出函数名,但各种信息存储在内存中,是一个占用。
      这样,当浏览器前进、后退或者刷新操作触发时,L o c a t i o n C h a n g e ( ) 函数先获得当前页面的URL ,然后从historyStorage中找到对应的各个AJAX信息并依次重新发出,就能获得正确的页面响应。这种方法不需要地址栏中给出函数名,但各种信息存储在内存中,是一个占用。
以上两种方法,都暂时地解决了AJAX 历史记录问题,但各有利弊,应据需要应变。当然,解决问题的方法多种多样,欢迎探讨。笔者相信,随着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。

 function showPageInfo( page_size , page_num )
function showPageInfo( page_size , page_num ) {
{ // 参数检查等
// 参数检查等

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

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

 }
}
 function LocationChange()
function LocationChange() {
{ // 第一步,获得当前页面URL 中的函数名
// 第一步,获得当前页面URL 中的函数名
 /**//* 注意,请不要使用window.location.hash,因为在笔者的使用过程中发现此种方法*/
/**//* 注意,请不要使用window.location.hash,因为在笔者的使用过程中发现此种方法*/ // 可能出现错误
// 可能出现错误 var currentLocation = window.location.href;
  var currentLocation = window.location.href; var function_info = “”;
  var function_info = “”; if( currentLocation && currentLocation.indexOf(“#”) > -1)
  if( currentLocation && currentLocation.indexOf(“#”) > -1)
 
   {
{ var index = currentLocation.indexOf(“#”);
    var index = currentLocation.indexOf(“#”); if (currentLocation.length > (index+1))
    if (currentLocation.length > (index+1))
 
     {
{ function_info = currentLocation.substring(index+1);
      function_info = currentLocation.substring(index+1); }
    } }
  }
 // 第二步,重执行函数
  // 第二步,重执行函数 if (function_info == “”)
  if (function_info == “”)
 
   {
{ // 为空时默认执行某函数或做其他处理
    // 为空时默认执行某函数或做其他处理 
    
 }
  } else
  else
 
   {
{ // 执行函数
    // 执行函数 try
    try
 
     {
{ eval(function_info);
      eval(function_info); }
    } catch(error)
    catch(error)
 
     {
{ 
      
 }
    } }
  } }
}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 )
function sendAJAXRequest( request_url , callback ) {
{ // 通过request_url 发出AJAX 异步请求,并通过callback执行响应
  // 通过request_url 发出AJAX 异步请求,并通过callback执行响应 
  
 }
} window.historyStorage = new Object();
window.historyStorage = new Object(); historyStorage[‘http://somesite/#showPageInfo(10,0)’] =
historyStorage[‘http://somesite/#showPageInfo(10,0)’] = [[request_url, showPageInfo_Callback]];
[[request_url, showPageInfo_Callback]]; function showPageInfo( page_size , page_num ){
function showPageInfo( page_size , page_num ){ // 参数检查等
// 参数检查等

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

 }
}

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

 }
} function saveHistory( url , ajax_info_array ){
function saveHistory( url , ajax_info_array ){ // 参数检查
// 参数检查

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

 }
}
 function getHistory( url ){
function getHistory( url ){ // 如果historyStorage中没有,返回空
// 如果historyStorage中没有,返回空 if ( typeof historyStorage[url] == “undefined” ){
if ( typeof historyStorage[url] == “undefined” ){ return null;
return null; }
} // 返回当前URL 对应的各个AJAX 请求,二维数组
// 返回当前URL 对应的各个AJAX 请求,二维数组 return historyStorage[url];
return historyStorage[url]; }
} function LocationChange(){
function LocationChange(){ // 第一步,获得当前页面URL 中的函数名
// 第一步,获得当前页面URL 中的函数名 var currentLocation = window.location.href;
var currentLocation = window.location.href; // 第二步,得到对应的各个AJAX 请求信息
// 第二步,得到对应的各个AJAX 请求信息 var ajax_info_array = getHistory(currentLocation);
var ajax_info_array = getHistory(currentLocation); if (ajax_info_array == null ){
if (ajax_info_array == null ){ return;
return; }
} // 第三步,重发各个AJAX 请求
// 第三步,重发各个AJAX 请求 for (var i = 0 ; i < ajax_info_array.length ; i++){
for (var i = 0 ; i < ajax_info_array.length ; i++){ // 遍历每个对应的AJAX请求信息,重新发出各个AJAX请求
// 遍历每个对应的AJAX请求信息,重新发出各个AJAX请求 sendAJAXRequest( ajax_info_array[i][0]
sendAJAXRequest( ajax_info_array[i][0] , ajax_info_array[i][1] );
, ajax_info_array[i][1] ); }
} }
}
以上两种方法,都暂时地解决了AJAX 历史记录问题,但各有利弊,应据需要应变。当然,解决问题的方法多种多样,欢迎探讨。笔者相信,随着AJAX 的发展,各种新问题、新办法都将出现,但只要抓住问题的本质都能够顺利解决。
-- 摘自《程序员06第12期》作者:陈英
 
                     
                    
                 
                    
                 

 
         
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号