代码改变世界

理想的 ASP.NET AJAX (Part 2 - Server Centric)

2007-07-31 23:32 by Cat Chen, ... 阅读, ... 评论, 收藏, 编辑

使用ASP.NET的话……

ASP.NET的最大优势就是组件化,在UI上更明确地说就是控件化,但这却为AJAX带来了不少问题。

首要问题是输出HTML不由我们控制。复杂的GridView不说,我们就来看简单的CheckBox,在你不对它设置任何样式属性和文本时,它是一个单纯的<input />,加上文本的话文本会被放在<label />中以便点击文本与点击<input />等效,如果再加上样式属性的话外围就再多一个<span />用于设置样式。如果你获得了一个CheckBox的ClientID,那么通过$get()(指document.getElementById操作)获取到的是<span />还是<input />呢?答案是那个<input />。

这时候真正的问题就出现了,假如我有一组CheckBox放在一个大的<span />容器里面,当我在客户端选中一个CheckBox的时候它就从这个容器里把自己删除掉(然后转移到另一个<div />容器中)。我们关注的只是这个<span />容器内发生的事情,当我们通过$get()获取到CheckBox对应的<input />后如何知道它的parentNode的<span />是属于CheckBox的呢还是作为容器的呢?这个问题可以通过对作为容器的<span />添加id解决掉,然而我们仍需要手动判断<input />的parentNode是不是CheckBox的一部分以便知道是否要将它也一起删除掉。当然,你会说我的CheckBox不设置任何样式属性,不存在此问题。然而你能够确保这个CheckBox永远不会被设置样式属行吗?甚至可能是通过Skin而被“意外”的设置了样式属性。总而言之,一个CheckBox是否有<span />以及是否有<label />都是不确定的,而且是不正交的设计选项——有很多不同的因素可能导致它们出现。

这类问题在我们仅仅针对ASP.NET服务器端编程时可以完全不管,而且还很爽的样子——看看,我们可以全然不知道客户端发生什么事的前提下,甚至连输出什么样的HTML也不用管,也照样能设计交互式的Web。然而当我们需要考虑客户端交互的时候,这就很不爽了,ASP.NET控件这种完全隐藏客户端细节的做法让我们难以对输出的HTML进行DOM编程。

通过ASP.NET AJAX解决?

在Atlas出来的时候,我确实希望过能够在一定程度上解决上述问题,最多我就由server centric改到client centric好了,并且我也一直尝试着用Atlas进行client centric的实验性项目,然而得出的结果一直都不怎么好。

我们继续以CheckBox为例吧,不过这次改变对象为客户端的CheckBox,也就是由<input />初始化而来的Sys.UI.CheckBox。Atlas在设计的初期为这些控件考虑了很多,虽然当时控件仅有简单的几个,这里面最强大的设计思想应该就是绑定(bind)了,允许用户自行定义绑定的实现方式,而不仅仅是如同ASP.NET服务器端那样仅仅支持简单的双向绑定及自定义的单向绑定。

然而很快Atlas进入了Beta阶段,为了1.0的及时发布中心转移到了UpdatePanel等的服务器端控件上,客户端控件再也得不到重视,功能不再更新,甚至为了兼容服务器端控件的更新而被迫放弃部分原有的客户端功能。这时候使用ASP.NET AJAX进行client centric开发就变得越来越不可能了。

另外,使用ASP.NET AJAX进行client centric开发还面临一个不够search engine friendly的问题,因为数据都是输出到客户端再展示的,因此HTML本身并不包含多少有语义的内容。我曾经尝试做一个类似WebPart效果的门户页,完全不使用WebPart而使用Atlas自带的拖放支持实现是没问题,虽然其自带的拖放并不完美而必须手工添加不少东西,然而我真正面临的问题是页面第一次输出应该输出什么。

方案1是真正的client centric,好像GMail那样,一出来是空白页,显示loading,这时候通过XHR去和服务器沟通然后再添加内容到页面上,这显然是完全不照顾搜索引擎的做法,对于GMail这样本来就不应该被索引的页面当然没问题,但是大众网站这样做就不好了。

方案2是服务器端先根据用户的Profile生成用户最后一次拖放保存的页面,之后的拖放及保存工作才由客户端负责处理。然而这意味着同一套逻辑我需要实现两次,可拖放的部件在服务器端要是一个Control,在客户端同样要是一个Sys.UI.Control,同事都具备串行化为Profile已经从Profile并行化还原的能力,这样工作量就大大增加了。

之后,从ASP.NET Control Toolkit我们可以看到,客户端Control的地位已经逐步被Extender取代了,因为Extender可以被看作是一组由Behavior、Web Service等元素组成的部件,它不再关心DOM元素在客户端作为一个Control的完整性和独立性,而仅仅是考虑这些DOM元素应该表现出来的行为从而将这些行为附加到DOM元素上面去。这样思想让我们面对上面二选一的情况时有了更好的选择——在服务器端应该注重Control的整体,而在客户端则仅关注Control输出的DOM元素应该具备的Behavior。因此,在上例中我不再需要管一个可拖放部件如何作为一个客户端Control存在,我仅需要知道这个部件是一个<div />元素并且它具有可拖放的Behavior就够了,而无论何时代表整个部件包括其内容的只有服务器端Control。

这样看来,如今的ASP.NET AJAX已经有相当的价值,前提是你有能力针对你的应用开发相应的Control和Behavior。至于未来的ASP.NET AJAX能够为开发者再提供多少便利,这已经很难说了,因为继续在ASP.NET AJAX上面增加client centric的支持,倒不如把精力投入到Silverlight的研发上面去,ASP.NET AJAX很可能不会再有重大更新版本,能把ASP.NET Futures中有价值的AJAX功能都RTM就已经算好了。

最后,如果大家觉得这个系列的文章好的话,欢迎订阅Cat in Chinese(feed)或Cat in dotNET(feed)。将来话题可能将转移到Silverlight,一个我认为比ASP.NET AJAX更具潜力的框架上。