2007年7月31日星期二

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

使用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更具潜力的框架上。

2007年7月30日星期一

理想的 ASP.NET AJAX (Part 1 - Client Centric)

怎样的AJAX才算是理想?

要说什么是理想的ASP.NET AJAX,就要先说说什么是理想的AJAX。事实上AJAX最不理想的地方在于search engine friendly以及bookmarkable,这两个问题有一定的相似性,要解决并不难,只是每一个系统中实现起来都不一样,因此难以提出一个统一的patterns来解决。

首先说说search engine friendly这一点吧,实际上使用了AJAX的站点有很多信息是搜索引擎无法索引到的,因为页面上部分的信息是在用户进行操作后才显示的,显示的这些信息有可能是固定的也有可能是经过XHR(XMLHttpRequest)查询服务器后动态显示的。如果是固定信息,那么在生成页面时就要考虑这些信息是否也应该输出为静态HTML。那么什么情况下该输出为静态HTML而什么情况下不该输出为静态HTML呢?我觉得应该看隐藏的内容是否就是页面语义的一部分。例如页面用于显示一张集体照,当鼠标指向照片上不同人物时将显示他们的姓名和有关的介绍,那么这些信息就应该输出为静态HTML了,因为它们是页面信息的重要组成部分,并且这些信息应该被搜索引擎索引到。

当然,事情并非任何时候都那么简单,有时候我们并不能简单判断某些信息是否应该属于一个页面,或者说一个URL。例如你使用一个页面来展示你的gallery,但并非一下子显示所有的照片,开头显示随机的10张图片,然后允许用户输入/选择tag来显示对应的照片,如果当前显示的照片超过10张还会自动分页。这时候怎么办呢?整个gallery输出的一个静态页包括所有的照片以及描述?这看起来不太可行。假如你的gallery有100个tag和1000张照片,同时这一个页面成功被搜索引擎索引了,用户通过搜索引擎来到gallery页,他怎么找到搜索引擎上匹配的图片以及对应的描述?他根本不可能知道他要找的信息藏在哪里了,100个tag中可能只有几个能让他想找的信息显示出来。

这时候比较好的做法就是将tag分离出来变成独立的页面,或者说是URL。gallery首页上的tag对于用户来说是交互式按钮,在不刷新的情况下直接筛选对应的照片;但对于搜索引擎来说那是链接,链接到代表该筛选状态的页面,并且在该页面上才索引到照片和描述。这样当用户点击特定的搜索结果是,进入的是已经筛选了的gallery结果页,也就必然能看到他搜索命中的信息。同时这也增加了bookmarkable的可能性,因为一个URL已经不仅仅代表一个页面,而代表一个页面的特定状态,因此状态变得bookmarkable。然而这时候普通的筛选结果还不是bookmarkable的,因为在进行根据tag筛选后URL是不会被改变的,因此我们必须通过JavaScript改变URL才能让页面变得bookmarkable。

与理想的距离有多远?

我们面临最大的问题就是URL与状态的对应,以及有关的存取。我们希望每一个indexable & bookmarkable的状态都对应一个URL,而且最好是meaningful的URL。好吧,我们先舍弃meaningful这一点来探讨状态问题,实际上我们已经有一些方法来保存状态。例如ASP.NET提供的ViewState就是一个很好的例子。很多ASP.NET的新手可能会觉得要理解如何正确使用ViewState并不容易,其实它就是用于保存view的state的。整台计算机其实也就是一个state machine,不过我们当前所关注的是与view有关的state,所以称之为ViewState。ASP.NET开头的策略是在服务器端处理一切,所以发明了ViewState并尝试让状态在客户端仅作持久不作修改。然而我们需要的正好是相反的,我们需要一个在客户端能修改的状态,并且以URL作为持久的方式。

首先,我们不可能好像ASP.NET保存ViewState那样保存数据到URL当中。即使我们舍弃了meaningful,ViewState的容量还是会超过URL的长度限制,因此希望好像ASP.NET那样每一个控件各自汇报自己的ViewState然后页面负责统一持久到URL是难以实现的。(当然,在ASP.NET AJAX中尝试一下这样的实现也未尝不可,虽然其提供的控件少之又少。)

既然我们不能够好像ASP.NET保存ViewState那样“放肆”地利用URL进行持久,那么我们就必须针对特定的应用来考虑持久的方式,这正是无法开发一个通用框架实现此功能的原因。例如上面所说的gallery,我们要人为的考虑使用tag作为区分view的一个状态参数,接着我们可能还要考虑当用户选中多个view之后会发生什么事,是不是简单的在URL里面叠加多个tag呢?例如gallery.html#fun+event的样子?那么它和gallery.html#event+fun是等效的,而两个URL会不会导致PageRank的分散呢?之后又如何加上分页参数呢?这一切问题不是没有答案,而是它们的答案都太具针对性了,因此不具有通用性,难以做成一个可复用的框架实现。

因此我们在这里能做到的最多就如ASP.NET Futures或者别的框架提供的history功能那样,提供一个有限容量的string空间给你,你要自己决定如何把状态转换为string然后提交给它保存,之后它保你实现跨浏览器的Permanent URL和history支持。

到此为止,我们站在client centric的角度讨论了AJAX现在面临的一些重要障碍,以及潜在的解决方案。下一part我们将从server centric的角度来继续研究这个问题,并探讨ASP.NET是否能在AJAX方面做得更好,又或者是别的Microsoft技术。继续关注本文章系列,欢迎订阅Cat in Chinese(feed)或Cat in dotNET(feed)。

2007年7月25日星期三

Hong Kong Trip

刚刚从4日3夜Hong Kong trip回来。事实上期末考的时候我就计划要去HK看看,一方面是去参观几所大学,例如科大、港大、中大啦,另一方面是去了解一下香港是一个怎样的城市,例如市民生活如何之类的。所以一放假我就去办通行证了,上个周末终于去了一次HK。

Day 1

第一天我们选择了不搭直通车而搭到罗湖,然后过关后转东铁到大学站,参观香港中文大学。中大座落在山上,面积超大,而且那天没有人带着游览,所以也不知道看什么好,很多区域也不能进去看,只是感觉的学校真的很大。很多教学楼都是傍山而建的,因此两边的出口在不同的楼层,高度差可以导致两个入口相差5层之多。

中大游览之后就去了尖沙咀看科技馆,正好碰到了“飞龙在天”的恐龙展览,于是就马上买了半价的学生票冲进去。有寄存服务真好,可以把书包扔下自由自在的看科技馆里面有趣的展品。展品有很多都是可交互的,虽然弱智了一些,因此很多小朋友在那里玩。

在科技馆中,和Lucy约好了7点到中环站,于是看完我们就搭地铁过去啦,本来想去ferry一下的也没时间了,结果去到Lucy才说最好等埋她同事然后带我们去吃好野,早知道就去埋ferry啦……

晚餐是去吃牛扒,好好味哦!然后回Lucy住酒店Poker,并且Lucy人品暴涨说可以让我和Singzy睡她的房间然后她睡sofa。说到Poker嘛,我发现自己不擅长这类要求下注的游戏,因为我总是没办法在短时间内算准可能出现的情况,我还是适合玩长线投资的game多一些,咔咔……

Day 2

第二天起来是Lucy还在睡觉中,貌似正中了“Hongkongese没有星期六早上”这个说法,因为大家星期五晚上都是用来娱乐的,那么星期六早上就是在睡觉中度过的。我和Singzy独自跑去Hollywood Rd,发现原来半山行人电梯早上是下行的,非常幸运,那就搭行人电梯下去了,结果原来Hollywood Rd也在“睡眠”当中——基本上没有开铺的。

既然Hollywood Rd没东西看,就搭车去香港大学咯,感谢昨天那么多人告诉我们23能到港大,就去坚道搭23了。去到港大东闸下车,发现10:00会有guided tour喔,而当时是9:00,面对的就是港大美术博物馆,当然是选择进去走一走并且等guide的出现啦。美术馆的叔叔很nice,后来出现的那位guide也很nice,于是我们就跟随着guide和一大堆同行的小朋友们游览了港大。

港大之后就是去深水埗看电脑,看到3款macbook的售价分别是8600HK$/10200HK$/11700HK$,也就和macx的代购价差不多,因此估计macx买得多所以买入价比单台的零售价还要低吧,这样它才有得赚。另外看到一本台湾翻译的Transcending CSS,译作《超越式CSS》,要180HK$,没有狠心买下来,回来发现这本2006年底出的书竟然emule上还没有pdf下载,极度后悔中……

午餐吃的是McDonald's,发现mid-size的薯条包装虽然和广州的差不多,但分量多很多啊,简直要撑死了。之后下午就去了黄大仙和科大。

香港科技大学的超级无敌大海景就是在吸引死人了,教学楼也是傍山而建,不过是直接面朝大海,而且整体规划所以效果很好,每一栋楼都能望海。可能因为科大已经考虑都会有相当多的游客来参观,所以有专门的纪念品商店、展厅等等,图书馆还在搞校史展览,新建的学校果然思想也新潮很多,很重视publicity。

在科大的图书馆看校史展览时很郁闷,每次一站定手机就开始震,然后冲出门口接电话,讲完又走回去。其中包括我姨妈的电话,约定了晚上到上环,于是就离开了科大。由于时间还有多,所以就没有搭地铁回香港,而是去ferry了——从尖沙咀的天星码头到中环码头。在渡轮上吹海风感觉很爽,可惜就是很快就到对面了。

Day 3

在罗湖过关的时候看到有书展的广告,所以第三天就去会展中心看书展了。超多人,龙尾排到很远,不过进场很有效率。实际上对比香港和广州,香港很多事情都要比广州高校很多,例如交通就如此,香港的街道上不会有“散步车”,能开的就很快地开过去,路上的行人能通行的时候也走得很快,总体来说应该是整个城市的节奏都很快吧。在广州的话,大家都习惯了慢慢来,不要那么紧张和冲动,所以效率也就完全不同了。

书展总共有5个hall,走完花费了5个小时。香港人看的书和我们看的书是不同的,貌似他们更多看书是为了休闲,至少他们平日工作已经够紧张的了,所以书一定要有相当趣味性才能看进去,太过单调的理论书是没有人买的。我不是说数学书,而是说例如基金书之类的。现在大陆够多人买基金书啦,很理论的书都有人买,但这在香港可能就是行不通的。

在书展当中我什么书都没买到,走了5个钟头啊……感觉比较可惜,真应该下狠心买一两本大陆绝对买不到的书回来看看,虽然价格肯定是大陆版的2~3倍,假如大陆会出版的话。promotion做得最厉害的一本书就是Harry Potter啦,最后一本哦,而且就在星期六全球同步发行,在走道一路看过去都是Harry Potter的标价,而且一个比一个低。另外一本很鬼死多promotion而且题材也很有趣的书就是台湾翻译的《把妹达人(The Game)》,内容看起来也很有趣,不过基于我不舍得花钱……所以还是选择了回来再找pdf。

书展之后超级疲劳的再次来到McDonald's,又是超大分量的薯条啊……已经吃到闻到薯条味就想呕了,一个月内不要再吃薯条。这餐是下午吃的,都不知道算午餐还是晚餐了,中午仅仅就在会展了面吃了几个墨鱼丸啊。

在McDonald's有很多菲佣,其实满街都是,因为是星期天,她们都出来聚会。忍受够吵闹的McDonald's之后我们决定跑去山顶看看,由于第一天碰到两个台湾来香港旅行的人问我们中环站如何走到缆车总站,因此我们步行到金钟站然后打一个站的地铁到中环站,打开guidebook一看才发现——原来金钟站和中环站步行到缆车总站的距离都是15分钟左右,晕掉了……走到缆车总站开始排队,我们开头想着去山顶看日落的,结果上到山顶看到的是“日落ed”——也就是说太阳刚好下山了,不过能吹吹风也不错。

在山顶也没有吃什么正经的晚餐,Starbucks喝了两杯东西就又要开始排队等缆车下山了,然后还是搭23回到坚道然后经半山行人电梯回hotel。貌似我们只知道23这条线,哈哈……

Day 4

最后一天,其实已经没有任何listed的行程了,于是大白天都在hotel里睡觉。Lucy说过中午请我们吃饭,所以11:30才起床去找Lucy。Lucy说搭12能够去到她上班的遮打大厦,我们从hotel下来连站牌都还没看清楚就有12停在身边了,那就上咯,看到哪个站合适就在哪个站下吧。HK的bus都当你是local,根本不报站,除非你主动问过司机你应该在哪下然后请他提醒你。作为一个旅游城市,HK路面的标示都超明显而且对游客有帮助,就是bus不报站这一点对游客很不friendly。

事实上Lucy也不知道去吃什么好,所以在兰桂坊随便挑了一家店就坐下来了,发现要一个4人套餐分量充足(废话,我们才3个人),而且每一款都很好吃。吃饱之后就和Lucy say goodbye然后去香港历史博物馆了——我们离开香港前的最后一战啊,因为博物馆可以寄存所以很适合背着行李去。

历史博物馆里面也碰上了guided tour,虽然整个tour并不让你有多少时间看清楚展品,不过guide的讲解也很精彩,能让你听到很多展品街市上没有说的事情,可惜我们时间有限,完成1个半钟的tour之后就没时间返回开头细看展品和演出了。很有趣的一件事情就是,两次积存行李时都被别人说“怎么你们的书包这么重啊”,其实就装了4天旅行的一些必需品啊,而且我们觉得也就是acceptable的程度,而非overloaded,看来HK的小朋友们的书包确实也不重啊,否则他们的daddy、mommy怎么会没见识过overloaded的书包呢。

17:55那班直通车,我们从红勘站离开了香港返回广州,不过因为列车晚点实际上我们18:30才上车。回到广州发现整个pace又慢下来了,所有事情又回到了我们习惯的速度。

2007年7月24日星期二

MVP 购物 coupon

使用Company Store的coupon购买的礼品收到了,我买了$90的硬件,包括webcam、headset和keyboard,剩下的$60用来买一些小礼品。小礼品大多数都是$10一件吧,买了一个大杯子给我爸,一个水壶给我妈,一个水瓶给Singzy,一个笔记本给Piggest,还有一件衣服是我自己的,之后剩下几$就买些钥匙扣之类的琐碎物。

我觉得这算是一种分享happiness的途径吧,而且这是一种保持自己happy的有效方法。当你拥有一些resource的时候,可以考虑不是把它们独占着,而是分享给更多人,同时又不引起什么竞争或者冲突,那就是最好的了。

2007年7月16日星期一

分享或索取新成立网站的邀请

如果你属于关注Web2.0的人,常常希望了解一下startup(新成立网站)的创意以及实现效果,又或者你仅仅是非常geek非常喜欢试新,你都经常需要各式各样的邀请以加入到所谓的private beta当中去。正所谓private beta,一开始的邀请当然只有开发者身边的朋友能拥有,而你和他们的距离是未知的,所以只能选择等待。

之前曾经有一些网站尝试去做邀请共享的服务,当时最热门的当然是GMail这样的邀请啦,于是大家就都把自己的GMail邀请链接投放到共享网站上,需要的人就直接获取。现在的大热变成Pownce了,甚至有人在eBay上出售邀请,不过也有免费的获取方法,那就是InviteShare。这个网站不仅仅共享Pownce邀请,还共享非常多其它网站或服务的邀请,例如JoostWeeWar的。你需要做的,仅仅是在InviteShare上面注个册,然后找到你想索取申请的网站,把自己添加到等候列表上,之后就等email通知吧。如果你觉得排队的速度太慢了,可以通过发送邀请来提高自己的优先级——从你手上成功发送出去的邀请越多,你排队的优先级就越高。

之所以说InviteShare,是因为我觉得它是一个做下游服务做得非常成功的例子。当某一种服务的数量越来越多的时候,例如需要邀请的Web2.0服务,那么它们的共性就会表现出来,这时候也就可能形成一个新的下游服务市场。这种情况往往是可预见的,例如Web2.0的private beta成为一种流行的服务推广方式,那么在恰当的时机推出下游服务也就是可行的。当然,事情的发展往往都是很快的,恰当的时刻一眨眼就过去了,因此我们需要一种相当敏捷的开发方式来实现我们能够提供的服务。

2007年7月14日星期六

今天收到 MVP 礼包了

礼包的寄送过程有点混乱,不过最终还是送到我手上了。拆开DHL的包装后,看到的是漂亮的MVP礼包盒子,诺大的纸盒打开以后发现包括一张证书、一枚别针、一本说明书和一个皮革质地的小盒子。

证书是很正式很传统的样子,有点像英文的毕业证书那样子吧,看字体就很有中世纪的feel。别针是MVP的徽标,比团徽重多了,针也粗多了,所以别在衣服上貌似不太合适,也不知道可以别在什么东西上。说明书则包括很多关于MVP身份的介绍性材料,当然还包括提醒你去消费那$150的Microsoft Company Store购物卷。事实上,真正有价值的东西都藏在皮盒里面,包括参加会议时可挂在脖子上的namecard(当然是注明MVP的了)、一个有MVP印记的皮质名片夹、一叠有MVP水印的memo纸、一支带激光的圆珠笔、一只皮质外壳的1G U盘。

对我而言,最有价值的应该就是那支笔了,以后做presentation就可以用它的激光在屏幕上随便指,这点比较爽。U盘是其次,我现在有个80G的移动硬盘,也有一张1G的SD卡可搭配读卡器使用,所以U盘并不显得是什么必须品,不过1G的空间以及漂亮的外表,还是很让人喜欢的。

差点忘记了说,在礼盒的底部有一个信封,装着的就是一式两份的NDA(Non-Disclosure Agreement),也就是保密协议。签完之后放到附送的信封里,直接投到邮筒里就是了,因为信封上说明了由收信方付费的。

2007年7月2日星期一

Microsoft MVP Award received

昨天晚上收到了邮件,告诉我获得了Microsoft MVP奖励,然后异常兴奋。要做的第一件事情当然是告诉身边的各位好友啦,特别是感谢那些在此事上曾经支持我的人。事情有时候很奇怪,至前几年热衷于上CSDN回答问题,去年开始将.NET有关的帖子集中到博客园并提升更新频率,这些都是比较“无心插柳”的事情,最后反而有结果了。

或许应该这样说吧,一些事情虽然我想要得到结果,然后过程却不是我真正想做的,在学校里压力不大,所以做事的时候不专心,总是很容易就进入了wandering的状态,结果当然不好。而另外一些事情,我做的时候很enjoy,虽然看不到什么结果,但积少成多最终还是会有结果。因此,在我的毅力得到充分的提升之前,或许我还是应该选择多做一些喜欢做的事情,而如果一件事情我发现无法强迫自己专心做下去那就应该放弃了。

其实人是否应该选择做一些自己不喜欢的事情,然后积累资源(例如金钱)再去做一些自己喜欢做的事情,这一直是个争论。有些成功人士认为这是应该的,但另一些则认为你必须从一开始就尽量选择自己真正喜欢的事情,那样才能把你的生命效率最大化,从而无论做出来是什么都是最成功的。Anyway,反正成功的定义是很personal的事情,你觉得自己活着怎样好就怎样好,没必要考虑太多社会评价,所以这个讨论上存在分歧也是很正常的。