攻城利器之微型框架 - Do
小站的Javascript开发是基于一个非常轻量的框架Do(Do是Douban的前两个字母),压缩后仅有961bytes,个头小但作用大,它是整个小站Javascript代码的管理者和组织者。
用它有什么好处:
1. 对各种用途的JS代码进行有条不紊的管理。JS的用途可以分为:
a. 模块级的JS。处理一个模块里的交互逻辑,它会随这个模块共存亡;
b. 页面级的JS。仅在特定页面里通用的处理,或负责模块间的交互等;
c. 全站级的JS。是重用度高的通用组件,通用的事件处理机制等。
全站级的JS是外联文件,页面级和模块级的JS是内联的。不可轻易混淆。在模板文件里,它们也有特定的位置:
<html>
<head>
<script>
// 这些都是全站通用的JS模块管理起来一目了然。
// Do.add的作用是建立一个模块库,并没有执行加载,所以可以把所有全部列在这里
Do.add('common', {path: 'http://img3.douban.com/js/site/packed_common0.js', type: 'js'});
Do.add('common-eventhandler', {path: 'http://img3.douban.com/js/site/packed_common_eventhandler0.js', type: 'js', requires: ['common']});
Do.add('setting-eventhandler', {path: 'http://img3.douban.com/js/site/packed_setting_eventhandler0.js', type: 'js', requires: ['common']});
Do.add('dialog-css', {path: 'http://img3.douban.com/css/ui/packed_dialog0.css', type: 'css'});
Do.add('dialog', {path: 'http://img3.douban.com/js/ui/packed_dialog2.js', type: 'js', requires: ['dialog-css']});
......
// 按模块名引用需要的模块。这里'common', 'common-eventhandler'是重用度最高的通用功能库和通用事件处理机制,所以head里就调用它们,由于是用非阻塞的加载方式不必顾虑性能上的影响。
Do('common', 'common-eventhandler');
</script>
...
<div id=”mod-id” class=”mod”>
<!-- 这里页面中的一个模块 -->
</div>
<script>
Do(‘所依赖的模块1’, ‘模块2’, function(){
// 模块的处理写在这个sandbox里。
// 这里写的JS通用范围就是这个模块。当重用这个模块时,html和js是绑在一起重用的。
// 这样维护起来就很清楚了。这里写再长的JS也不必担心会阻塞后面内容的渲染。
});
</script>
......
在基类模板中,会有一个${self.bottom_script()}的位置,它布在</body>的前面。这里是放页面级的JS。上面说过了,页面级的JS是解决某个页面内的问题,它应该和页面绑在一起维护。(提 问:如果多个页面存在相似的处理,是否按全站级JS处理?答:如果仅在2〜3个页面里重用,重用度还不够高,还不能上升为全站级的。如果后期,越来越多的页面用到它,再对其进行重构升级为全站级的)
这种代码等级制度另一个好处就是把精力花在重要代码上。
2. 版本管理变得很轻松。
因为对通用模块的引用都是按模块名引用的,当某个JS组件升级了,只需要更新配置就好了。所以不必担心更新不彻底的问题。
3. 对网站性能有利。
所有JS文件都采用异步加载后,对服务端来说一定会产生更多并发请求。这个问题要这么看,网站前端性能是优先考虑的,网页打开的快不快是用户重要的使用体验。带来的服务端的问题会有一些手段来平衡。总之,这方面的优化不宜过早做,根据使用情况应对。在小站的实际应用上,Do的效果还是不错的,看图:
Firefox 3.6下:
这个小站的规模(http://site.douban.com/106336/)是比较有代表性的。上图注意几点:蓝线的位置我很满意,DOMContentLoaded表示整个页面已经渲染完,红线表示Load完。这说明越快出现蓝线,用户看到页面打开越快,也就是说用户感知的性能越快。红线出现的早晚已不影响用户的使用,所以它并不重要。上图中你会发现jquery.min.js(第6行)和下面的请求是并行的。我试了多个小站,触发DOMContentLoaded都不超过2秒,大部分在1秒内完成。这一点我很满意了。另附IE下的情况:
IE 7下:
IE8下:
其实如果专门优化小站的性能还是有不少事情可以做的。但不必太早做优化,顺其自然就能达到不错的性能就好,下一步再精益求精。
Do在小站中的应用也是一次很好的实践检验。
用它有什么好处:
1. 对各种用途的JS代码进行有条不紊的管理。JS的用途可以分为:
a. 模块级的JS。处理一个模块里的交互逻辑,它会随这个模块共存亡;
b. 页面级的JS。仅在特定页面里通用的处理,或负责模块间的交互等;
c. 全站级的JS。是重用度高的通用组件,通用的事件处理机制等。
全站级的JS是外联文件,页面级和模块级的JS是内联的。不可轻易混淆。在模板文件里,它们也有特定的位置:
<html>
<head>
<script>
// 这些都是全站通用的JS模块管理起来一目了然。
// Do.add的作用是建立一个模块库,并没有执行加载,所以可以把所有全部列在这里
Do.add('common', {path: 'http://img3.douban.com/js/site/packed_common0.js', type: 'js'});
Do.add('common-eventhandler', {path: 'http://img3.douban.com/js/site/packed_common_eventhandler0.js', type: 'js', requires: ['common']});
Do.add('setting-eventhandler', {path: 'http://img3.douban.com/js/site/packed_setting_eventhandler0.js', type: 'js', requires: ['common']});
Do.add('dialog-css', {path: 'http://img3.douban.com/css/ui/packed_dialog0.css', type: 'css'});
Do.add('dialog', {path: 'http://img3.douban.com/js/ui/packed_dialog2.js', type: 'js', requires: ['dialog-css']});
......
// 按模块名引用需要的模块。这里'common', 'common-eventhandler'是重用度最高的通用功能库和通用事件处理机制,所以head里就调用它们,由于是用非阻塞的加载方式不必顾虑性能上的影响。
Do('common', 'common-eventhandler');
</script>
...
<div id=”mod-id” class=”mod”>
<!-- 这里页面中的一个模块 -->
</div>
<script>
Do(‘所依赖的模块1’, ‘模块2’, function(){
// 模块的处理写在这个sandbox里。
// 这里写的JS通用范围就是这个模块。当重用这个模块时,html和js是绑在一起重用的。
// 这样维护起来就很清楚了。这里写再长的JS也不必担心会阻塞后面内容的渲染。
});
</script>
......
在基类模板中,会有一个${self.bottom_script()}的位置,它布在</body>的前面。这里是放页面级的JS。上面说过了,页面级的JS是解决某个页面内的问题,它应该和页面绑在一起维护。(提 问:如果多个页面存在相似的处理,是否按全站级JS处理?答:如果仅在2〜3个页面里重用,重用度还不够高,还不能上升为全站级的。如果后期,越来越多的页面用到它,再对其进行重构升级为全站级的)
这种代码等级制度另一个好处就是把精力花在重要代码上。
2. 版本管理变得很轻松。
因为对通用模块的引用都是按模块名引用的,当某个JS组件升级了,只需要更新配置就好了。所以不必担心更新不彻底的问题。
3. 对网站性能有利。
所有JS文件都采用异步加载后,对服务端来说一定会产生更多并发请求。这个问题要这么看,网站前端性能是优先考虑的,网页打开的快不快是用户重要的使用体验。带来的服务端的问题会有一些手段来平衡。总之,这方面的优化不宜过早做,根据使用情况应对。在小站的实际应用上,Do的效果还是不错的,看图:
Firefox 3.6下:
这个小站的规模(http://site.douban.com/106336/)是比较有代表性的。上图注意几点:蓝线的位置我很满意,DOMContentLoaded表示整个页面已经渲染完,红线表示Load完。这说明越快出现蓝线,用户看到页面打开越快,也就是说用户感知的性能越快。红线出现的早晚已不影响用户的使用,所以它并不重要。上图中你会发现jquery.min.js(第6行)和下面的请求是并行的。我试了多个小站,触发DOMContentLoaded都不超过2秒,大部分在1秒内完成。这一点我很满意了。另附IE下的情况:
IE 7下:
IE8下:
其实如果专门优化小站的性能还是有不少事情可以做的。但不必太早做优化,顺其自然就能达到不错的性能就好,下一步再精益求精。
Do在小站中的应用也是一次很好的实践检验。
求DO源码
桐球DO源码
http://img3.douban.com/js/do2.js
你们这群小孩子,一点也不知道自己自足
强力支持一下,哈哈!
这么一段代码同时出现下划线分隔和驼峰命名两种风格.
最后面还突然出现一个匈牙利式~
已经闭包隐藏的变量依然使用了下划线开头~
这其中可有玄机.? 求解答~
弱弱地问一句,什么是匈牙利式?
哈哈
Do.add的定义那个就是.
这个克军讲过
这个很赞!
YUI3的思想,记得D2的时候克军讲过
Do 在git 上面有源码 http://github.com/kejun/Do
最好能把jQuery裁剪一下,或者重新搞一些常用方法。
@老田
不建议动它,以后升级不好办。尽能减小它对性能的影响
可惜是单线程下载,如果不改前端代码倒是可以用服务器combo的方式优化
居然在这里看见超哥。。。。
想知道上面上测试结果,是要用什么测的
@LS
firefox下用firebug,ie下可以试试filder或者httpwatch
文中提到页面的重构,或者说是 js 的重构,很想知道如何做呢?也像 java 一样有好用的 IDE 来帮助重构吗?
和那个没关系的,java也是依靠框架来做MVC的。
如果多个页面级别js方法产生通用性,这时想把这一坨方法提高到公用级怎么闹?难不成要一个页面一个面的改?
@Whyme.Lyu ”闭包隐藏的变量依然使用了下划线开头“,这只是一种约定俗成
很给力...好好学习下。
Do(‘所依赖的模块1’, ‘模块2’, function(){
// 模块的处理写在这个sandbox里。
// 这里写的JS通用范围就是这个模块。当重用这个模块时,html和js是绑在一起重用的。
// 这样维护起来就很清楚了。这里写再长的JS也不必担心会阻塞后面内容的渲染。
});
这一段不明白。
刚好写了一个$.cache的东西,然后使用这个框架加载,结果发现在chrome9下出现了ready不能加载的问题。
(function($){
$.cache = {};
// cache换成任意字符串都能触发ready
})(jQuery);
$(document).ready(function() {
alert('catched');
});
回ls,firebug是调试的好帮手
我错了,问题无法重现了。
嘿嘿,使用do能够将速度提升多少?
还有,能不能将do的path扩展下,因为每一个js文件就写一个add挺麻烦的,能把几个js归并到一个模块库下吗?
提个反馈:ff和chrome下对404的url不会触发onload,触发的是onerror,ie还是onreadystatechange。
n.onload = n.onreadystatechange = n.onerror = function ()
当某个在队列中间的url由于服务器的问题返回404的时候,不会触发onload时间导致它后面的url不被加载。
ex:Do('https://img1.doubanio.com/js/jquery.minxxx.js', 'https://img2.doubanio.com/js/jquery.js');
Firefox/3和chrome10下第二个js无法加载,ie8正常。
do中的require代表的是不是先加载require的js文件然后再加载本文件?
怎么测试下,感觉加载顺序没有改变~
学习了
我之前在项目中使用了此,非常不错。现在突然发现一个严重的问题,Do.js并没有开源……
有没有想过抛弃jquery,重新写js库?
> 我来回应