Fan's blog Rotating Header Image

Posts under ‘Web Development’

复杂web应用的非侵入Javascript之路

就跟Javascript是世界上被误解最深的语言一样,HTML也是一种古老的原本简单的技术,现在却被大量甚至过度使用,旧有技术并没有考虑到方方面面,也没有鼓励和抑制某种用法。这样对于复杂应用,旧有的编程模型就得大量的改进。 1. 混合 早期的页面都是逻辑和展示混合在一起的。比如<button id=’sub’ onclick=”submitData();”/>。 2.分开 采用事件绑定。比如在jquery中,$(‘#sub’).bind(‘click’, function(){…}),这样逻辑和显示分开了。这些绑定的代码大多写在页面的onload里面,但这不代表所有的情况。 3.javascript模块化 对于web2.0或者Ajax使用很多的应用,用户感觉他始终在一个页面操作,比如Gmail,左侧的栏是一直在的,不会出现整个页面刷新的情况,多数时候是页面上的某一小块更新内容。 比如上面页面右侧区域的内容是根据用户左侧点击操作而动态更新的,这块区域的事件绑定就需要动态的,而不能在整个页面装载时绑定。这样页面各个区域以及整个页面的javascript其实是可以分离开的。 这种web页面结构已经有点走向传统C/S界面开发了,但是html+javascript开发起来难度就大很多。为了避免走向过度复杂,当程序走到这一步需要打住:1)保持简单,避免过度设计(特别是对于小的应用)。只有很少的应用需要做到Gmail的复杂程度。简单是web的本质。2) 重构页面,重构操作模式,不能把C/S的开发思路硬搬到web开发。3)使用RIA之类的胖客户。 模块化为减少全局变量的使用铺平了道路。全局变量不仅指<script>title = ‘All’;</script>这种声明的变量,通过document.getElementbyId也是对全局变量document的访问。这种方式很容易的可以访问页面其他的元素,鼓励了页面元素间的依赖关系。这种细粒度的依赖织成复杂的关系网,当测试复杂程序时会非常困难。 减少全局变量则使另外一个问题突出来:模块间的交互。在有全局变量时候,模块间可以使用这种共享变量来进行通信,现在得另外找道路。event和Observer模式是一个降低模块间耦合度的好方法,jQuery和dojo对这方面都有关注,javascript的函数式语法处理这些得心应手。有些模式比如Front controller也可以达到这种效果。 3.组件 当web应用不可避免的走向B/S模式时,B/S中的组件模型也被复制到html中。典型代表是Dojo,也即Digit widget,还有Ext JS。页面被划分的更细,组件有独立的javascript(事件处理),css。显然这种方式是web开发的理想方式:封装更好,重用性更好。 不过web的优点在于简单和标准,组件对已有编程模型改变太大(侵入式),是一个“框架”而不是“工具”,适合建立复杂的web应用,比如企业应用。一般的应用采用jQuery这种辅助性js库开发更轻量级,更灵活。

Javascript闭包(回调函数)的问题

回调函数网络上一大堆,这里举个实际的例子: <script src=”../javascripts/jquery-1.4.2.js”></script> <script language=”javascript”> $(document).ready(init); function init(){ console.log(“….init”); var sd = new Date(); $(‘.list’).each( function(){ this.onclick= function(){ console.log(“you click,haha”); process(sd); }; }); }; function process(youDate){ console.log(“I got it:” + youDate); } </script> <div id=”nav”> <div>11</div> <div>22</div> <div>33</div> </div> 逻辑很简单:当页面加载时注册回调函数,当点击div时激发回调函数。这里注册和激发是异步的。 注意回调函数可以访问上一级的成员变量sd(或许在FP中称为运行上下文),这个跟java的observer模式中的内部类可以访问容器类的this指针一样,这就是说当你点击div时,原来已经产生的时间变量sd还在,这样打印出来的时间都是旧的时间。这是closure的灵活强大的地方(《JavaScript: The Good Parts》里面说,“使用闭包来进行信息掩藏的方式,它是另一个减少有效全局污染的方法”。真知灼见啊!,否则又要使用大量的全局变量),但是跟Java一样,这里由于引用的关系可能会出现垃圾回收的困难,java用弱引用来处理这个问题。这篇文章Memory leak patterns in JavaScript有描述,里面提到了circular references,大概意思是javascript对象引用了DOM对象,而DOM对象反过来又引用了javascript对象。上面代码改成: $(‘.list’).click( function(){   console.log(“you click,haha”);   [...]

从“在HTML中嵌入数据”到Dojo的组件模型

有时候web页面会包含一些隐藏数据,或者”非显示”(non-visible)数据。比如Google Reader中的博客文章列表: 这里很多的Ajax操作都需要文章的唯一id,而这个id是没有显示在页面的。这种情况应该很常见。 老的方法可能是用类似<a onclick=”process(’243242′);”/>这种方法,不过现在很少用了。这把control逻辑和显示混合到一起了,元素少的时候还不明显,元素多了会有很多的process(‘xxxx’)这样的重复逻辑。(我后来发现jquery binding的时候就可以传递数据,sigh,也算是一种方法吧。) 一般在HTML中嵌入数据的方法为利用HTML Element的class或者attribute。 利用class的例子为: <div class=”entry entry-2″/> <div class=”entry entry-3″/> 利用attribute的例子为: <div value=”10″ maximum=”100″ minimum=”0″/> 这里面maximun和minimun都是非标准的属性。 当然也可以使用hidden的element来持有数据,这些方法的缺点在于把数据嵌入到显示中来,导致页面混乱。再者这些非标准的方法使页面丧失语义性,这里详细的讨论了这个问题。HTML5中有一节Embedding custom non-visible data改进了这个问题,这样写: <div data-ship-id=”92432″ data-weapons=”laser 2″ data-shields=”50%” data-x=”30″ data-y=”10″ data-z=”90″> 个人感觉也其实也很难看,谈不上优雅,比XML差不少。或许HTML本来其实不适合做这种事情,语义网也许是解决的根本之道,唔,扯远了。 这种嵌入数据的需求在Ajax流行下越显突出。web2.0提倡的所有操作在一个页面完成,这样最后页面会很复杂,需要承载更多的数据。 如果从server端返回HTML,这些HTML数据的较之XML和json就复杂多了,因为这样的HTML是数据和显示的混合。特别是在像表格这样的数据比较集中的地方,HTML这种格式不适合javascript对其进行数据的后期处理。(只有在表格这样的地方,这个问题才比较突出) 使用json后,页面的rending又成了一个问题。一般可以用json templete来处理,比如json-template。这样数据是独立出来了,view也独立出来了,不足的地方是缺少一个binding的机制,否则在数据和view间的每次操作都得手工建立对应关系。 Dojo里面的dijit/widget对于这种情况给出了很好的解决方案。widget作为一个对象或者组件,数据可以直接用类似成员变量保存在对象中,不用很怪异的把它嵌入到html中,然后取值又得费劲的操作DOM。 使用组件的概念后,OOP的优点也继承到javascript中来。组件消除了全局变量,对HTML页面进行了隔离,这样规范后就不会出现原来的document.getElementById满天飞的操作。由于不是在总的document之下,一个组件的定义中就不能假设另外的组件的存在,这样,组件之间通过event来耦合,这比getElementById来直接访问是一个很大的进步。

Rails开发总结

用Rails开发煮豆(zudou.net)这个小应用有段时间了,对于一个从Java转过来的人说,变化还是很大的。 总的来说,Java一般偏向企业应用,而Rails偏向互联网应用,两者开发思路很不同。Java用来处理数据或者后台应用比较多,而互联网处理页面更多点,用到很多Ajax和HTML来对界面精雕细琢。企业应用的用户需求是固定的、文档化的,而互联网则开放很多。 从技术上来说,Rails语言很轻巧,上手很快,刚开始会觉得原来的Java开发效率实在是太低了,但是如何有效的调试程序则是个大问题,遇到程序异常很容易束手无策,具体原因一则新手对架构内部和语言细节不熟悉,二则不同平台有不同调试方法,这方面经验是跟平台绑定的,不能共享的,不同的平台的常见异常都不一样,Java下容易出现Nullpointer,Rails则千奇百怪。从另一个方面说,Java语法更严格,文档也很多,所以虽然开发效率低,但是按部就班,也不会很慢。 而对于一个框架开发者来说,调试也要纳入到易用性中来,错误信息要充分,简单而且明了,很多错误信息读起来不知所谓,对于一个满头是包的人来说,拿着这些仅有的救命稻草,如果没有多少信息该是多么有挫折感。