概述:
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。
2。URL 映射法
URL 映射法的提出是基于对AJAX 本质的这样一种认识:
AJAX 请求的本质是请求服务器资源的URL,以及执行AJAX响应的回调函数(如果是POST方法,还应包括POST 的内容;如果是同步请求,还应包含同步请求的标志,这里一般认为是异步请求)。因此,只要记录下AJAX 请求的URL 及其回调函数,就可以重新执行该AJAX 请求。
对不同页面,以地址栏URL 为标识,记录下其对应的各个AJAX请求URL 和回调函数。在浏览器前进、后退或刷新等操作时,先通过当前地址栏URL 获得对应的各个AJAX请求URL和回调函数,然后重新发出这些AJAX 请求,即可获得正确的页面结果。
函数sendAJAXRequest通过请求URL及回调函数callback发出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_info 是一个字符串,由函数名及相应参数组成的字符串
var function_info = “showPageInfo(page_size , page_num )”;
changeURLLocation(function_info);
// 业务逻辑
}
而函数changeURLLocation修改页面URL,将函数信息作为URL 的Hash部分。// 参数检查等
//function_info 是一个字符串,由函数名及相应参数组成的字符串
var function_info = “showPageInfo(page_size , page_num )”;
changeURLLocation(function_info);
// 业务逻辑
}
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_info 是一个字符串,由函数名及相应参数组成的字符串
// 将函数信息作为新URL 的Hash 部分
Var current_url = “http://somesite/#” + function_info;
// 将新URL 写入地址栏
window.location = current_url;
}
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 上有所体现,即需要修改对应函数的某些参数,这就增加了程序的复杂度。// 第一步,获得当前页面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)
{
}
}
}
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请求信息二维数组。{
// 通过request_url 发出AJAX 异步请求,并通过callback执行响应
}
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()的实现应改为:[[request_url, showPageInfo_Callback]];
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中:// 参数检查等
//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);
// 业务逻辑
}
function saveHistory( url , ajax_info_array ){
// 参数检查
// 存储在historyStorage 中
historyStorage[url] = ajax_info_array;
}
与其对应的,函数getHistory()通过当前地址栏URL 得到包含各个对应AJAX 请求信息的二维数组:// 参数检查
// 存储在historyStorage 中
historyStorage[url] = ajax_info_array;
}
function getHistory( url ){
// 如果historyStorage中没有,返回空
if ( typeof historyStorage[url] == “undefined” ){
return null;
}
// 返回当前URL 对应的各个AJAX 请求,二维数组
return historyStorage[url];
}
同样,需要函数LocationChange(),但此时函数实现改为:// 如果historyStorage中没有,返回空
if ( typeof historyStorage[url] == “undefined” ){
return null;
}
// 返回当前URL 对应的各个AJAX 请求,二维数组
return historyStorage[url];
}
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信息并依次重新发出,就能获得正确的页面响应。这种方法不需要地址栏中给出函数名,但各种信息存储在内存中,是一个占用。// 第一步,获得当前页面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] );
}
}
以上两种方法,都暂时地解决了AJAX 历史记录问题,但各有利弊,应据需要应变。当然,解决问题的方法多种多样,欢迎探讨。笔者相信,随着AJAX 的发展,各种新问题、新办法都将出现,但只要抓住问题的本质都能够顺利解决。
-- 摘自《程序员06第12期》作者:陈英