为了充分利用 AJAX,必须在单一页面中包含其中的全部功能,或者是至少包含大部分功能。这称作单页界面 (SPI) 模型。在 SPI 模型中,浏览器与 Web 应用程序的所有交互只能在一个页面的范围内进行。此方法对 Web 来说是一种创新,但对于 Windows
®和桌面开发却并不陌生。SPI 其实模型就像是具有主(并且唯一)窗口的 Windows 应用程序一样。
另一方面,如果屏幕读取器超出了 DOM 中的初始页面加载事件范围,它们通常不会发出任何新信息。但您可以使用一些技巧让屏幕读取器知道有些内容已经发生了变化。最有效的一招就是在更新后的 DOM 树的根目录将 tabindex 设置为 -1。但这只是一个小技巧,可访问性的解决需要更为通用的 AJAX 解决方案。
其中的一种可行解决方案是 W3C 正在制定的被称为“可访问富 Internet 应用程序”(ARIA) 的新标准,它主要由 HTML 标记的特定读取器扩展组成。一些比较流行的客户端 AJAX 库已经开始支持某些 ARIA 功能了。但一般来说,这并不仅仅是有关 AJAX 可访问性兼容标准的可用性问题。它还常常涉及服务器控件实际生成的内容(标记和脚本)。
对于脱机应用程序是怎样的情况?许多开发人员认为脱机 AJAX 应用程序不可能实现,因为 AJAX 应用程序仍然完全基于 Internet。在我看来,这种说法虽然并非完全错误,但也有点过于简单化了。现在,几乎所有 Web 应用程序(AJAX 和非 AJAX)都基于 Internet 或 intranet。
但是,非 AJAX 应用程序的确可以脱机工作。例如,对于历史记录管理,启用脱机导航的魔力完全在于浏览器。在传统的 Web 应用程序中,每个 HTTP 请求都由浏览器来管理。如果没有可用连接(在引发 HTTP 404 错误之前),浏览器会在其本地页面缓存中进行查找。
使用 AJAX 构建胖客户端是一个最严峻的挑战。胖客户端可以是分布式企业系统的前端,也可以是业务线应用程序的表现逻辑层。它还可以是 IT 部门决定作为 Web 应用程序而公开的独立应用程序。这些应用程序无论是发布到 Internet 上还是限制在 intranet 内,都需要常规桌面 UI 所具有的丰富性和速度。
与 Windows 开发相比,在交互性和响应性方面 Web 开发都是一种倒退。利用 AJAX,您最终可以使用其中的工具(和环境条件)向本质上不同的模型演化。但其中存在着不容忽视的折衷。一方面,有数百万的用户和开发人员已习惯了使用旧的 Web 及其历史模式、脱机导航、收藏夹、单一操作、页面转换和永久链接等。另一方面,您采用的却是 AJAX 及其并行操作模式和单一自动更新用户界面功能。在这种情况下,对于用户任务,AJAX 模型需要真正的恢复模型,而不仅仅是使用浏览器的历史记录和页面导航功能。
要对 SPI 模型编写代码,需要使用一组新的设计模式。
图 4列出了一些最流行的 AJAX 模式。正如您所看到的,其中大部分都侧重于用户界面技术和排列,但您也应该注意到,此列表中并不包括一些已在 Microsoft
®AJAX 客户端库中实现的 AJAX 流行模式和实践。例如,如果您使用 ASP.NET AJAX,则不需要 AJAX 存根、跨浏览器 JavaScript 模型或调用跟踪模型,因为所有这些功能都是预置的。
<script type="text/javascript">
var timer = null;
function pageLoad()
{
if (timer === null)
{
timer = new Samples.Tasktimer(5000,stopTask);
timer.start();
}
}
function pageUnload()
{
if (timer != null)
timer.stop();
}
function stopTask()
{
// Stop the clock
var clock = $find("<%= Timer1.ClientID%>");
clock._stopTimer();
AskIfTheUserWantsToContinue();
}
function AskIfTheUserWantsToContinue()
{
// Ask if the user wants to continue
var answer = window.confirm(
"Is it OK to continue with the clock?");
if (answer)
{
// Restart the task
var clock = $find("<%= Timer1.ClientID%>");
clock._startTimer();
// Restart our own timeout engine
if (timer !== null)
timer.start();
return;
}
}
</script>