模块化的前端开发 — OOFE

在 webrebuild 年会北京站,蒋定宇 分享的模块开发对我的影响不小,也做了不小的尝试,觉得可取的地方很多。

既然尝试了,就分享下我对模块化的前端开发的理解吧,当做一份试验报告吧。

一,开发中的代码污染

在更新比较频繁的产品线上,项目随着时间的推移,产品的需求也就变得相当的频繁,PM需要我们前端工程师修改的地方也将变得越来越多,而这些时间里,公司的产品线中,维护代码的人可能也更替几次了,而每一个工程师要读懂以前的代码的难度也会随着产品线的时间以维护人员的更替增加而越来越大。维护一个好几手的代码,可能也不是一般新工程师喜欢的事情,大多工程师一般都会选择避过老代码而直接开发新的代码;还有就是多人一起开发时,大家每个人的代码书写习惯以及技术的参差不齐,导致很多重复功能的开发,就这样,产品线的前端走向一个到处是染的,杂乱,重复的功能代码,参差不齐的袋子。

问题:缺少框架与文档的共同约束,没有良好的前端架构

二, HUB 和 UI Thread

hub的工作方式:

HUB是一个多端口的转发器,当以HUB为中心设备时,网络中某条线路产生了故障,并不影响其它线路的工作,也不会导致HUB故障。

重点:client个体的单一独立化,hub做为转发器,让client之间实现交互。

UI thread 的中文意思大概是 用户界面线程吧,就是指一个用户界面的前端表现线程,或者说前端代码被浏览器解释的过程?我也说不清楚,我们还是用它可意会,不可言义的名称UI Thread 来理解吧 ^ ^ 。举过小例子来说一下吧。

请看下面的代码:

<input id="btn" style="padding: 3px; border: 1px solid #ccc; background-color: #eee;" type="button" value="请猛击我" />
 
<script type="text/javascript">
var btn = document.getElementById("btn")l
btn.onclick = function(){
     btn.style.border = "1px solid #f00";
     btn.style.backgroudColor = "#f22";
}
</script>

从这个UI Thread例子中,我们知道浏览器提供了一个用于JS,CSS,HTML相互交互和通信的机制。浏览器做为一个事件派发环境,为UI提供事件的注册,初始化,和派发等。

三, 模块化开发

合并Hub 与 UI Thread 建立稳固的前端架构:

了解了Hub的工作方式以及UI Thread 后, 结构,表现,行为都各自建立一个以模块(类似 hub 中的 client)为单位切分的环境。其实就是所谓的OO,面象对象。

概念:OOCSS,Javascript模块化开发

OOCSS是最近大家关注的比较多的一个话题,提倡面向对象的开发

在OOCSS里,一个CSS对象由4部分组成:

  1. 可能是一个或多个DOM节点的HTML
  2. 由包含节点的class名开始的CSS样式声明
  3. 类似于背景图片和显示用的sprites组件
  4. JavaScript监听,广播及运行与对象关联的方法或行为

看一段代码:

<div class="mod">
       <div class="head">
           <h1>Block Head 1</h1>
           <h2>Block Head 2</h2>
       </div>
       <div class="body">
           <div class="hd">Block Head</div>
           <div class="bd">Block Body</div>
           <div class="ft">Block Foot</div>
       </div>
</div>

该对象是一个class为mod的模块。
包括4个部件节点(不能独立于模块外,包括3个区块,head,body,foot,在模块开发中head和foot属于可选择的区块)。

OOCSS的性能提升:

  1. 高度重用的CSS代码,只需要很少的CSS代码,意味着:
    • 更小的文件,从而更快的传输
    • CSS代码在站点页面中调用的比重增大则有希望被复用或被浏览器缓存
  2. 就浏览器而言更少的重绘和布局计算
    • 单个页面,CSS规则复用的越多,渲染引擎花在“computed values”的计算时间越少
    • 手动增加的”extending”类,重写更少的规则,这再一次意味着引擎做很少去应用规则

对于具有特性的对象,你要可以使用ID定义样式,比如非常特殊的header menus,此时你可以在用ID来扩展对象的专一性。

Javascript模块化开发:

这个已经是很多库都在做的事情了,但是在这里提到的Javascript模块化开发是指建立一个OOCSS中的对象间相互跨模块或者说跨对象交互通信,以OOCSS对象为对象的开发模式,模仿UI Thread 编写单一模块(对象) 的线程

还是拿类似于蒋定宇提到的那个例子来说明下吧!

例子页面:猛击此处>>

采用Hub的工作方式以及UI Thread 实现这两个模块间的交互:

图片展示模块(对象)的代码如下: [ 我把这实现模块交互的Hub取名为VUI ]

/* * 图片展示模块
* @param {String} Module Name
* @param{Object} Module
*/
 
VUI.add("photo-show-icon", {
//模块初始化:监听img-src-change命令
 
      init: function(mod){//传入当前mod
            mod.listen("img-src-change");
     },
//信息处理:接受到img-src-change命令时,更新UI
 
     onmessage: function(eventType, data){
           switch (eventType) {
                case "img-src-change":
                this.updateUI(data);
                break;
           }
     },
//更新UI函数
     updateUI: function(data){
           var img = "&lt;img src='" + data + "' width='100%'/&gt;";
           document.getElementById("photo").innerHTML = img;
     }
});

这个模块(对象)的行为层就写出来了,他本身决定了他能干什么,如何交互,是一个独立的对象。

联合OOCSS与上面的模块(对角)行为层来看前端开发中的模块对象:

  1. 模块(对象)的功能是完整的,非片段且独立的。
  2. 每个模块(对象)拥有自己的html,css,javascript
  3. 一个模块(对象)放到页面中,不会改变其它对象的表现,结构以及行为
  4. 一个模块(对象)被移除了,不会对其它对象构成影响,支持热插拔
  5. 一个模块(对象)可以做单元开发,单元测试,可以独立工作。
  6. 不同的开发维护人员,开发维护过程中只针对模块(对象)进行开发和维护,且开发不会对其它对象造成影响,出现污染代码的概率较少。

对于模块(对象) hub 环境的实现,需要一个底层的对象线程处理,我这里实现了一个比较简单的实现库,取名VUI

上面的图片切换显示模块就是根据这个库实现的:

代码在这里:请猛击些处>>

四, 模块化开发的优化处理

模块(对象)化开发产生的问题:

  1. 因为开发中是基于对象开发的,每个对象建立独立的文件,实现通过组合模块(对象)来组建一个页面文档。必会产生多的css与js的引用,从而导致页面过多的请求数。
  2. Javascript各模块(对象)是独立的,需要一个Hub使他们通过互相交互通信

问题的解决:

  1. 多文件的问题,使用文件合并压缩,css sprite等技术可以完美解决。
  2. Javascript 各模块的交互通信,可以依据产品开发一个底层的事件派发环境来实现,如上我提到的比较简单的实现库:VUI。

报告到此结束:猛击下载查看图片展示例子的全部开发文件>>

Html5 (1)

最近在开发产品线的无线版本,主要支持苹果移动设备平台ipad与iphone。记录平时遇到的问题。

一,标签

html5 新增的标签,nav,header等标签在Safari 下都是比较正常地块级元素,而在Firefox跟Opera都是inline。

Safari对video的支持不是想象中那么好,特别是 safari 4.0* ,载入比较慢,好几次让我们以为他不支持。

(只有Opera支持mp3的音频。

Firefox不支持m4v的视频格式。)

二,地理定位

Safari 不支持

开发中其实我们的目标是只需要保证Safari浏览器,不过算第一次面对html5的项目,所以大家特别上心,拿着各浏览器测试。虽然才开始一天,不过大家的激情都蛮高的,嘿嘿。

querySelector & querySelectorAll

Javascript 选择器总是让你的javascript开发捉襟见肘,成查找DOM成为开发的第一道难题,原生的ECMAScript查找DOM的方法不多,而且繁琐。需要做更为精准的DOM查找,需要做一系列的正则匹配,不仅复杂,而且效率低。

很幸运的是目前大多数浏览器都支持了浏览器原生的DOM查找方法:querySelector & querySelectorAll,可以让你像使用CSS一样查找DOM,效率高,而且方便。

浏览器支持现状:

IE: 8.0+,Firefox,Chrome,Safari,Opera

示例:

查找ID:

document.getElementById("test");

使用querySelector 或者 querySelectorAll

document.querySelector("#test");
document.querySelectorAll("#test")[0];

 

document.querySelector("div#test");
document.querySelector(".test>p:first-child");

流水帐

转战北京了。遇以了一些以前没遇到的事。

租房好痛苦,坐地铁上班。

不是一个人住了,跟别人合租,还不是很习惯很多东西。

还好现在的公司是早上10点上班,不然不知道我住得那么远,这懒觉怎么满足啊。

刚到北京,很多事都得重新来,所以在学习。

想想从珠海离别后,好久没看书了,挺惭愧的。准备入手几本书,可是下单的时候,居然不知道现在所住地的地址,很是悲哀。

一年里换了三个工作地方,其实对我来说挺悲哀的。感觉我这颗年轻的心需要好好安静下来了,所以跟现在的公司签了三年,但愿这次守得长些,感觉我现在仍然是那么的燥动啊。

农民工进京

辞职了,离开呆了将近8个月的珠海,打算回家休息一段时间。回家之前,去了趟北京,农民工进京的感觉就是小周周还没变,还是那么牛逼,天朝的空气质量不是很好,房租很贵,人很多…长城很难爬。

这次进京虽然来回都挺折腾的,都没有买到卧铺,很是辛苦,不过有人报销车费,还是挺高兴的,吼吼吼吼。

发几张PP吧。

希望下次农民工还有机会进京。

2010中国城市榜之我家 兴义

城市名称:兴义

城市口号:中国最美的地方

城市地址:地处贵州、广西、云南三省 (区)结合部,位于南(宁)贵(阳)昆(明)经济圈的中心,是泛珠三角经济区对东盟出口的腹地,是我国西南地区一个重要的交通枢纽、商贸中心和物流中心。

独特迷人的城市景观

兴义市山川秀丽,人文名胜丰富,自然景观奇特,旅游资源多姿多彩。地处老城区的八一公园,被兴义市的母亲河——湾塘河挽入怀中,翠竹殷 殷,花草繁茂,常常让人流连忘返。

安全和谐的宜居环境

兴义有着优美的自然风光、丰富的人文景观、厚重的自然遗产、浓郁的民族风情。同时,兴义市区位优越、交通便利、能源充足、资源丰富、民 风纯朴、投资环境优良。

友好开放的城市民情

论你是在街头漫步,还是在景区景点观光旅游,你都会感到兴义浓郁的民族风情扑面而来,兴义市民热情的招呼、真情的微笑会让你对兴义流连 忘返。

完善的城市旅游设施

兴义加快了建设旅游景区宾馆、饭店、购物中心及其他旅游服务配套设施。完善旅游公路沿线电力、供水、通讯、银行、医务、餐饮、厕所、环 保等设施,形成功能齐全、服务便捷、安全周到的旅游环境。

后话:虽然本人觉得“中国最美丽的地方”这个口号有点土,而且还有些恶心,有些嚣张,不过目前身在广东珠海这个严热的地方,我特想家,想兴义夏日的清凉,想兴义的夏日里可以去田野闲散的时光。在珠海,夏日风景虽好,却根本无法出门游玩,会中暑的。想想在兴义的十多年,那里还真是我呆过的城市最美丽的地方,气候逸人,风景美丽。

有谁不爱自己的家乡呢?

投票地址: http://chinesecity.cri.cn/


一个简单的动画类[2k]

本来打算看世界杯的,可是网速不是很争气,就拿这个时间把上次某个公司笔试中写的一个很简单的动画类扩展一下,2K[未压缩]。不算大,一般的需求都有了。遗憾的是还没有暂停功能。

var Animation = function(el, styles, ops) {
	if (!el && arguments.length <= 0) {
		return;
	}
	var el = typeof (el) == 'string' ? document.getElementById(el) : el, from = {}, m = {}, motion, ops, cb, ing = {}, m_type = {// 动画类型公式
		line : function(t, b, c, d) {
			return c * t / d + b;
		}
	}, des = {
		type : "line",// 动画类型
		fs : 35,// 帧娄
		timer : 400,// 动画运行的时间
		ing : null,// 动画中回调
		callback : null	// 回调
	}, css = function(el, styles) {// 
		if (typeof (styles) === 'string') {
			styles = styles.replace(/ /g, '');
			if ((/\-/g).test(styles)) {
				styles = styles.split("-");
				styles = styles[0] + styles[1].substr(0, 1).toUpperCase()
						+ styles[1].substr(1);
			}
			if (el.currentStyle) {
				return el.currentStyle[styles];
			} else if (window.getComputedStyle) {
				return window.getComputedStyle(el, null)[styles];
			}
		} else {
			for ( var name in styles) {
				name = name.replace(/ /g, '');
				el.style[name] = styles[name];
			}
		}
	};
	for ( var p in styles) {
		from[p] = parseInt(css(el, p)) || 0;
		m[p] = parseInt(styles[p]) - parseInt(from[p]);
	}
	for ( var p in des) {
		des[p] = ops[p] || des[p];
	}
	motion = function(t, d) {
		var moving = function() {
			t += 1;
			if (t > d) {
				clearInterval(mmm);
				if (des.callback) {
					des.callback();
				}
				return;
			}
			for ( var p in styles) {
				var dis = typeof (des.type) == 'function' ? des.type(t,
						from[p], m[p], d) : m_type[des.type](t, from[p], m[p],
						d);
				el.style[p] = dis + "px";
				ing[p] = dis;
			}
			if (des.ing) {
				des.ing(ing, el);
			}
		};
		if (t < d) {
			var mmm = setInterval(moving, des.timer / des.fs);
		}
	};
	this.add = function() {
		motion(0, des.fs);
	};
};

使用时新建一个实例就可以了,当然也可以直接当函数来用。

var motion = new Animation("test",{//新建实例
        width:"500px",//宽度由当前变化到500
        height:"400px",
        left:"100px",//左位置由当前变化到100
        top:"300px",
        "marginLeft":"120px"//左边距由当前变化到120
},{
        type:function(t,b,c,d){//t:开始帧数,b:开始位置,c:移动距离,d:持续帧数;默认type为线性动画
              //自动义动画计算公式,
        },
       fs:35,//帧数
       timer:400,//动画持续时长
       ing:function(p){//动画执行中回调,返参为当前元素动画参数,width,left,top之类},
      callback:function(){//回调}
});
motion.add();//添加动画

示例:点击查看

提供一些动画公式:[整理于互联网]{把function拷到type那里就可以用了}

       easeIn: function (t, b, c, d) {
            return c*(t/=d)*t + b;
        },
 
        easeOut: function (t, b, c, d) {
            return -c *(t/=d)*(t-2) + b;
        },
 
        easeBoth: function (t, b, c, d) {
            if ((t/=d/2) < 1) {
                return c/2*t*t + b;
            }
            return -c/2 * ((--t)*(t-2) - 1) + b;
        },
 
        easeInStrong: function (t, b, c, d) {
            return c*(t/=d)*t*t*t + b;
        },
 
        easeOutStrong: function (t, b, c, d) {
            return -c * ((t=t/d-1)*t*t*t - 1) + b;
        },
 
        easeBothStrong: function (t, b, c, d) {
            if ((t/=d/2) < 1) {
                return c/2*t*t*t*t + b;
            }
            return -c/2 * ((t-=2)*t*t*t - 2) + b;
        },
 
        elasticIn: function (t, b, c, d, a, p) {
            if (t === 0) { 
                return b; 
            }
            if ( (t /= d) == 1 ) {
                return b+c; 
            }
            if (!p) {
                p=d*0.3; 
            }
            if (!a || a < Math.abs(c)) {
                a = c; 
                var s = p/4;
            } else {
                var s = p/(2*Math.PI) * Math.asin (c/a);
            }
            return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
        },
 
        elasticOut: function (t, b, c, d, a, p) {
            if (t === 0) {
                return b;
            }
            if ( (t /= d) == 1 ) {
                return b+c;
            }
            if (!p) {
                p=d*0.3;
            }
            if (!a || a < Math.abs(c)) {
                a = c;
                var s = p / 4;
            } else {
                var s = p/(2*Math.PI) * Math.asin (c/a);
            }
            return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
        },
 
        elasticBoth: function (t, b, c, d, a, p) {
            if (t === 0) {
                return b;
            }
            if ( (t /= d/2) == 2 ) {
                return b+c;
            }
            if (!p) {
                p = d*(0.3*1.5);
            }
            if ( !a || a < Math.abs(c) ) {
                a = c; 
                var s = p/4;
            }
            else {
                var s = p/(2*Math.PI) * Math.asin (c/a);
            }
            if (t < 1) {
                return - 0.5*(a*Math.pow(2,10*(t-=1)) * 
                        Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
            }
            return a*Math.pow(2,-10*(t-=1)) * 
                    Math.sin( (t*d-s)*(2*Math.PI)/p )*0.5 + c + b;
        },
 
        backIn: function (t, b, c, d, s) {
            if (typeof s == 'undefined') {
               s = 1.70158;
            }
            return c*(t/=d)*t*((s+1)*t - s) + b;
        },
 
        backOut: function (t, b, c, d, s) {
            if (typeof s == 'undefined') {
                s = 1.70158;
            }
            return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
        },
 
        backBoth: function (t, b, c, d, s) {
            if (typeof s == 'undefined') {
                s = 1.70158; 
            }
            if ((t /= d/2 ) < 1) {
                return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
            }
            return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
        },
 
        bounceIn: function (t, b, c, d) {
            return c - Tween['bounceOut'](d-t, 0, c, d) + b;
        },
 
        bounceOut: function (t, b, c, d) {
            if ((t/=d) < (1/2.75)) {
                return c*(7.5625*t*t) + b;
            } else if (t < (2/2.75)) {
                return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
            } else if (t < (2.5/2.75)) {
                return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
            }
            return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
        },
 
        bounceBoth: function (t, b, c, d) {
            if (t < d/2) {
                return Tween['bounceIn'](t*2, 0, c, d) * 0.5 + b;
            }
            return Tween['bounceOut'](t*2-d, 0, c, d) * 0.5 + c*0.5 + b;
        }

雨后小池中的睡莲

去食堂吃饭回来,看到小池中的睡莲甚是漂亮,拿出手机拍了几张。见片[只裁未作任何效果处理]:

祝贺’有灵工作室’成立

有灵工作室

有灵工作室

有灵工作室,得名于《陋室铭》里的山不在高,有仙则名,水不在深,有龙则灵之句。寓意是大家为龙的子孙,传承其灵性。又有伟周言:所谓有灵即是有灵 性,我们有灵性,我们的作品将更有灵性。有灵工作室的朋友大都散居在全国各地,然冥冥之中有一线贯穿南北。

聚首于斯,如幽灵般不可捉摸。有灵,是聪明与智慧。

我们大都是求学楚地之学子,在这里相识,并且相互承诺,将坚持,坚定,坚信的为缔造一个有灵的游戏世界拼尽全力。
更希望苍天有灵,知天行健,君子自强不息的精髓,将荣光赐予有灵工作室的我们。

我们以有灵工作室为陋室,谱一首陋室铭,望志同道合之士能与我们一起,缔造一个崭新的未来。
希望一切有灵!

陋室铭
唐•刘禹锡
山不在高,有仙则名;水不在深,有龙则灵。斯是陋室,惟吾德馨。苔痕上阶绿,草色入帘青。谈笑有鸿儒,往来无白丁。可以调素琴,阅金经。无丝竹之乱耳,无 案牍之劳形。南阳诸葛庐,西蜀子云亭。
孔子云:“何陋之有?”