关于Ajax模式与Web单页模式(SPI)的几点思考

Ajax的几个原则

      与传统的Web应用程序所采用的开发模式相比,Ajax对Web开发人员而言意味着一种模式转变。Ajax立足于一些新的原则和规则:

  •  
    • Ajax的第一个原则是用户将“纯数据”发送到Web服务器,然后接收更多的“纯数据”。
    • Ajax的第二个原则是用户自行协调操作,这将略过主机浏览器及其单页面请求/响应机制。
    • Ajax的第三个原则是客户端代码利用从服务器接收到的“纯数据”来全面负责对用户界面的更新。

ASP.NET的“部分呈现”技术

     在ASP.NET中,“部分呈现”是一种在处于ASP.NET WebForm体系结构中仍可实现某些Ajax功能的途径。ASP.NET“部分呈现”是一种非常“智能”的技术,属于传统的WebForm回发模型。简单说来,使用“部分呈现”的页面,其回发体系结构和页面生命周期与非Ajax的ASP.NET页面。普通的ASP.NET页面回传机制如下图所示:

wps_clip_image-664

“部分呈现”与普通的ASP.NET页面不同之处在只在于客户端的侦听器阻止浏览器的默认操作(表单提交),并用XMLHttpRequest装载的HTTP请求来替代HTTPRequest。这种方式不但可以节省用户的整页刷新时间,同时还可以节省开发人员在学习Ajax新的开发体系中所投入的时间(微软总是过于照顾开发者,以达到控制最终PC用户的目的)。ASP.NET使用“部分呈现”的Ajax页面回传机制如下图所示:

wps_clip_image-767

 

      毋庸讳言,“部分呈现”在用户界面上提供了更好的体验(至少不用整页刷新了),所以非常适合页面内部回发,例如,对网格进行分页、变更选择后调整用户界面或就地编辑记录。但是,如果的应用程序是围绕很多独立的页面设计的,那么从一个页面到下一个页面的跳转仍需要整页加载。

      由于“部分呈现”只是一种更“智能”页面回发,因此它具有与WebForm相同结构限制。例如,每个浏览器窗口只能有一个待决请求。对于当前的Web应用程序(提交表单、获取新页面)模型而言,此限制不会产生太大问题,但是,随着我们对Ajax认识的拓展和应用的深入,自然而然会期望用户可以同时执行多个活动。仅依靠部分呈现,用户是无法同时执行两个异步操作并将其完成的。“部分呈现”只能每次执行一个请求操作以保持“视图状态”的一致性(一致性仍是模型不可或缺的组成部分)。这一点严重违背了Ajax原则的第一个要求:异步。使用部分呈现,可以实现JavaScript驱动的回发,但每次只能有一个。如果在前一个操作完成之前触发了第二个操作,则待处理操作将被中止,以便新操作可以继续执行。可通过编程方式将这一后进得胜策略改为先进得胜模型,从而使当前操作仍保持活动状态,而新操作将被系统取消——实质上这仍然是每次只能执行一个操作。

 

单页界面模型(SPI)

      为了充分利用Ajax,必须在单一页面中包含其中的全部功能,或者至少包含大部分功能。这称作单页界面 (SPI) 模型。在SPI模型中,浏览器与Web应用程序的所有交互只能在一个页面的范围内进行。此方法对Web来说是一种创新,但对于Windows桌面应用程序的开发却并不陌生。SPI模型就像是具有主(并且唯一)窗口的Windows应用程序(SDI)一样。

      在SPI模型中,主页面是可以独立加载、更新和替换的一些可视元素的组合,如下图:

wps_clip_image-1759

 

      通过这种方式,可以不必在每次用户操作后重新加载整个页面。在任何时候,都只显示与应用程序当前阶段相关的可视元素和内容。其他所有内容均被隐藏;但只要应用程序流程中需要用到它,它就会显示出来。

      SPI模型自然会启用许多交互性很强的功能,其中包括就地编辑、上下文相关的用户界面、即时用户反馈提示以及异步操作等。但SPI模型并不仅仅具有出色的性能或响应能力,它的主要优势在于针对用户体验方面做的重大改进。

      不过,即使SPI拥有如此多的优点,我们还是应该清楚,使用SPI模型设计新应用程序是一项具有挑战性的工作,因为没有任何现成的模式和最佳实践。

      为了构建纯Ajax应用程序,我们需要一个良好的用户界面小组件库来提供各种效果和特殊行为。我们需要一个内容丰富而又可以自定义的文档对象模型 (DOM),此模型使用标准的W3C DOM作为其基础引擎,允许我们定义自己的应用程序模型。最后,我们还需要一个服务器和客户端框架,以便轻松有效地开发用户界面和源代码脚本。最好还准备一些用于调试和测试所有这一切的工具。

      在 SPI 模型中,主页面指向同一应用程序中的HTTP端点。它执行远程代码,但不会重新加载整个页面。而且,它还会使用生成HTML和脚本的控件来更新用户界面。这些控件非常智能,可以生成它们需要的许多JavaScript代码。很明显,只需要一些Javascript就可以轻松实现这种显示/隐藏技巧。

 

单页界面模型的缺点

      虽然SPI模型带来了交互性更强的用户体验,但同时也引发了诸多问题,例如可搜索性、历史管理、可访问性和脱机支持等。多年以来,一直使用永久链接来跟踪Web页面。搜索引擎只需将一组关键字映射到一个或多个URL即可构建其业务。此模型的工作前提是假设Web应用程序中每个状态都对应一个页面和一个不同的URL。

      如果SPI模型处在Ajax中,则此假设不再有效。如果所有一切(或大部分)都发生在同一页面中,则没有URL转换来标记不同的状态和不同的站点内容。因此,没有将内容(和关键字)与唯一URL相关联的简单方法。SPI模型的这一特性会影响可搜索性以及历史管理(例如,使用“后退”和“前进”按钮的能力)。浏览器历史记录最终可能会变为过时的概念,它们是传统静态Web模型的搭档。但是我们必须要知道,用户对这些按钮实在是太熟悉了,简单地禁用它们并不是一个可行的办法。

      可访问性是Ajax应用程序的另一个大问题。大多数流行的屏幕读取器在处理通过DOM脚本生成的任何内容时都面临很大的困难。按照设计,所有形式的Ajax(包括部分呈现)都严重依赖于DOM脚本。因此,我们需要通过其他途径来解决可访问性问题。我们需要使用<noscript>标记静态信息。

 

可访问的富Internet应用程序(ARIA)

      W3C正在制定被称为“可访问富 Internet 应用程序”(ARIA) 的新标准,它主要由HTML标记的读取器扩展组成。一些比较流行的客户端Ajax库已经开始支持某些ARIA功能了(目前RIA领域两大巨头:Adobe的Flex、微软的SilverLight)。

      对于脱机应用程序是怎样的情况?许多开发人员认为脱机Ajax应用程序不可能实现,因为Ajax应用程序仍然完全基于Internet。在我看来,这种说法虽然并非完全错误,但也有点过于简单化了。现在,几乎所有Web应用程序(AJAX 和非 AJAX)都基于 Internet 或 intranet。但是,非Ajax应用程序的确可以脱机工作。例如,对于历史记录管理,启用脱机导航的魔力完全在于浏览器。在传统的Web应用程序中,每个HTTP请求都由浏览器来管理。如果没有可用连接(在引发HTTP 404错误之前),浏览器会在其本地页面缓存中进行查找。

      Ajax应用程序不同于传统的Web应用程序,它使用XMLHttpRequest对象而非浏览器引擎来发送HTTP请求。对于支持脱机的Ajax应用程序,您只需为XMLHttpRequest对象赋予访问浏览器缓存的权限或赋予创建并管理其自身已访问页面的缓存的能力即可。某些Ajax框架已开始引入此功能。但这并非是一件容易的事,因为它涉及从JavaScript代码访问磁盘(而Javascript这一功能在设计之初就被阉割了,智能调用浏览器对应的COM对象来访问本地文件)。

 

AJAX模式概述

      在Google等等公司的努力和倡导下,下一代基于Web的应用程序将直接面向Ajax,而且会更多直接面向富Internet应用程序 (RIA)。起初,引领这一革新版本的是以下三大类应用程序:基于 HTML 的传统站点、为了将多个系统集成到一个基于 Web 的前端而构建的混合应用程序、胖客户端。

      对于基于 HTML 的传统站点,部分用户界面加载(部分呈现)是实现 AJAX 的一种简便方法,它对现有代码和技能的影响非常小。

      而混合程序的概念本身未必适合于Ajax和改进的用户交互。它只是一种从各种来源收集数据并将其一并放入一个统一而又一致的用户界面中的方法。此操作也可以在传统的服务器对服务器的情况下执行。但是坦率地说,Ajax可以使其更加简单有效。

      使用Ajax构建胖客户端是一个最严峻的挑战。胖客户端可以是分布式企业系统的前端,也可以是业务线应用程序的表现逻辑层。它还可以是IT部门决定作为Web应用程序而公开的独立应用程序。这些应用程序无论是发布到Internet上还是限制在intranet内,都需要常规桌面UI所具有的丰富性和速度。

      与Windows开发相比,在交互性和响应性方面Web开发都是一种倒退。利用Ajax,开发人员可以使用其中的工具向本质上不同的模型演化。但其中存在着不容忽视的折衷。一方面,有数百万的用户和开发人员已习惯了使用旧的Web及其历史模式、脱机导航、收藏夹、单一操作、页面转换和永久链接等。另一方面,开发人员采用的却是Ajax及其并行操作模式。在这种情况下,对于用户任务,Ajax模型需要真正的恢复模型,而不仅仅是使用浏览器的历史记录和页面导航功能。

      要对SPI模型编写代码,需要使用一组新的设计模式。下表列出了一些最流行的Ajax模式。其中大部分都侧重于用户界面技术和排列:

模式

目标

浏览器端模板化此模式建议使用HTML模板,它们将使用从远程HTTP端点检索的数据动态填充。此模式建议开发者设置自己的模板层,而不要基于每个请求动态为数据重新生成HTML布局。此模式是HTML Message模式的替代方案。
跨域代理此模式对可访问的、公开提供的服务运行服务器到服务器连接,并将数据传回客户端。在客户端浏览器中,不允许Ajax应用程序连接到页面域外部的任何URL。但是,同一域内的本地代理可以轻松地从任何位置获取数据并将其返给调用方。
检测信号由于许多Ajax应用程序可能会在客户端执行大量操作而不传回信息,因此可能需要通知服务器指定客户端仍处于活动状态。此模式建议客户端应用程序要定期上载“检测信号”消息,以指示应用程序仍在载入状态并在浏览器中正常运行。
HTML消息远程HTTP端点通常会返回要集成到现有DOM中的客户端JavaScript Object Notation (JSON) 数据。此任务只能通过Javascript来完成。但是,如果客户端代码特别复杂或考虑到性能问题,我们可能希望从服务器返回HTML(数据和布局)而不是纯数据。
微链接Ajax主要用于在同一页面内执行大量活动。那么如何引用外部内容(即在传统Web应用程序中会转到不同页面的内容)?我们需要一种页内超链接或称为微链接。微链接是对标记块的引用,它通过服务器调用进行检索然后再插入到页面中。微链接可以是HTTP端点,也可以是Javascript命令对象的方法。
点播JavaScript这是流行的延迟加载模式的Javascript版本,通常用于数据访问层。在页面初始化过程中下载所需的全部Javascript 可能会对性能产生影响,从而拖慢整个进程。通过采用按请求来加载 Javascript 文件的方法,可以使页面获得更快的加载速度,同时还不会对功能产生影响。
页面安排由于大部分应用程序的活动都发生在同一个页面中,因此需要更新页面的内容并随着上下文内容的变化来显示最新信息。此模式只建议使用DOM来添加/删除或显示/隐藏元素以反映其状态的转换。
定期刷新浏览器会定期安排请求以获得最新信息,并用它来刷新用户界面。
弹出框此模式代表Web版本的模式/无模式Windows对话框。弹出框由一些HTML内容组成,它们显示在现有内容的前面,显示时间可以很短,也可以一直显示直到用户将其取消。
预取模式此模式建议预测最可能的用户操作并预先获取所需的数据。实现此模式需要付出一定的代价:毕竟它只是猜测,有时可能会预测错误。尽管可以有效地提高感知性能,但如果实现效果不佳或者由于严重的服务器带宽损耗致使达不到理想情况,则此模式也可能会带来性能损失。
进度指示器此模式用来监视服务器操作的进度。其思路是,服务器操作将其自身的进度写入一个共享位置,而客户端监控服务则可在进度刷新时进行读取。
提交限制Ajax的其中一个潜在缺陷是在单位时间内可能会生成过多的服务器请求。如果出现这种情况,则说明存在着明显的可伸缩性问题。此模式建议使用定时器将数据定期上载到服务器和本地缓存或队列中,以将请求累积起来。
超时如果从客户端执行一些重量级操作(如流式或定期刷新),则要想保证每个连接的客户端都真正使用此应用程序会有一些困难。此模式建议使繁重操作超时,在用户提出明确请求时再恢复。
唯一URL对于反映不同状态的应用程序的不同部分,此模式允许为其分配不同的URL。此模式通常用于支持 Ajax应用程序中的历史记录。
虚拟工作区服务器需要尽快响应请求,但由于带宽原因,它可能不必返回所有可用的数据。此模式建议构建一个虚拟用户界面,即使数据在客户端只存在一小部分,也可以实现使所有数据都可用的目的。应用程序会负责根据需要下载数据并将其缓存到本地。

 

 

唯一 URL 模式

      URL是Web的基础。用户可以将中意的URL保存下来以供将来参考、可以按照URL所指开始新的内容体验,此外还可以使用URL回到先前的状态。在Ajax和SPI模型中,应用程序可以在单个URL中完成许多任务。这将使Web体验的核心支柱面临彻底的改变,即:应用程序的离散状态是由不同的URL来标识的。

      浏览器会在用户浏览时构建其各自的URL缓存。但如果使用Ajax,许多操作都不通过浏览器,因此不会被缓存到访问过的URL列表中(在此列表中可驱动“后退”和“前进”菜单)。另一方面,客户端浏览器不会提供将URL添加到列表中的Javascript代码编程模型。在现有列表中,浏览器对象模型只会提供向前和向后的导航方法。

      “唯一URL”模式为每个重要的应用程序状态都分配一个唯一的、含义鲜明的URL。例如,如果用户在Ajax页面中通过单击来编辑某个值,则新URL会被添加到浏览器缓存中,即使此操作是通过XMLHttpRequest在同一页面中执行的。

      可使用以下 Javascript 来更改URL而无需重新加载页面:

window.location.hash = stateInfo;

      此代码的作用是将以#作为前缀的片段添加到URL中,如下所示:

http://www.contoso.com/shopping.aspx#edit-1234

      使用此模式时,URL实际上是在启动任何给定的Ajax操作时发生改变的,因此可以通过浏览器来跟踪应用程序状态的变化。

      但是,要执行的操作远不止捕获URL这样简单。如果浏览器被定向到基于哈希值的URL,它首先会加载主URL,然后再查找具有此哈希名称的页面段。在Ajax上下文中,哈希名称并不指向实际的页面段,而是指向代表当前状态的特定于应用程序的信息。例如,edit-1234 可能表示正在编辑的项目其ID是1234。实际格式完全取决于开发者。

      如果浏览器找不到适当的段,则它将忽略URL哈希值。这样,用户会加载该页面,但可能不是以预期的应用程序状态。此外还需要另一个技巧。开发者应截取页面的onload事件、分析URL、提取哈希值并运行将页面置于期望状态所需的 JavaScript 代码,如下所示:

window.onload = function() {
    checkAndParseURL();
}
 
checkAndParseURL() {
     var state = window.location.hash;
     restorePage(state);
}

 

 

      在ASP.NET 3.5扩展所提供的历史记录支持中也采用了类似的方法。ASP.NET 3.5扩展中将以添加到ScriptManager控件的新属性和事件的形式显示出来。但最终,它将是“唯一 URL”模式的具体实现。

      另外还应注意,基于URL哈希值的技巧对IE无效,因为IE无法识别出URL哈希值的变化,除非是内嵌帧。实际上,所有浏览器在处理段导航方面所表现出的行为特点都是互不相同的。

 

超时模式

      Ajax的其中一个最大优点是可以实现实时页面更新。但是,实时更新如果被误用,可能会成为应用程序的严重威胁。假设某个用户显示了一个活动页面,此页面每隔几秒钟轮询一次服务器以更新一些内容。如果该用户离开几个小时,但没有关闭浏览器。这样做产生的结果是,页面不断发送请求,给服务器带来大量(而且无用)的工作负荷。

      如何能够确定客户端会话是否超时?在服务器上存在着会话超时,而在Ajax中也存在着不容忽视的客户端会话。要检测客户端会话的结束,需要检查在给定时间段内是否有用户活动(例如单击和敲击)。监视键盘和鼠标活动的任务可能会非常繁重;我们通常都采取一种基于定时器的方法,这种方法不但简单而且很有效。

      要根据定时器检测会话的结束,应将客户端定时器设置为经过指定秒数(实际当中更有可能是分钟数)之后过期、停止正在执行的任务并弹出一个警告框。如果用户对提示做出响应,则会照例重新开始处理。

      下面代码描述了超时模式的本质。在页面中嵌入了一个时钟。此时钟可使用 UpdatePanel 中的 Label 控件来获取,并由 Timer 控件定期更新,如下所示:

<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>
protected void Timer1_Tick(object sender, EventArgs e)
{
    Label1.Text = DateTime.Now.ToLongTimeString();
}

      此时钟代表一项繁重的任务,可能会造成服务器请求溢出。其思路是设置一个定时器,定期询问用户是否确实想要继续运行时钟。超时代码首先会停止时钟,然后显示消息框。得到用户响应后,时钟重新启动。

      此代码利用 $find 函数来定位 ASP.NET AJAX 组件,在本例中,它还是 ASP.NET 定时器服务器控件的客户端对象模型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值