Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
RubyLouvre committed Apr 20, 2013
1 parent bc269cc commit e2ae6c1
Show file tree
Hide file tree
Showing 6 changed files with 4,540 additions and 4,425 deletions.
112 changes: 112 additions & 0 deletions doc/avalon/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<!DOCTYPE html>
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<title>avalon</title>
<script>
window.$$path = location.protocol + "//" + location.host;
document.write('<script src="' + $$path + '/mass_merge.js"><\/script>');
document.write('<script src="' + $$path + '/doc/scripts/common.js"><\/script>');
</script>
</head>

<body>
<article>
<center><h3>avalon</h3></center>
<p>一个完美遵循“操作数据即操作DOM”理念的MVVM框架</p>
<p>自从jQuery将人们从DOM的桎梏中解放出来,得以有空研究其他。
轮子创造活动也日益频繁,只要有了一个带头者,就有无数个跟风,后浪推前浪,挡也挡不住,
前端的轮子大多数是开源的,因此可以互相借鉴,会越造越好。后端的分层构架就是这个时候引入前端的。
首先是基于jQuery的javascriptMVC(现在更名为CanJS),然后是设计更清晰的Backbone。
Backbone火的时候,就像PrototypeJS那样有无数复制品。
</p>
<p>这时候偷偷崛起了三个MVVM框架,knockoutjs, emberjs, angularjs。 </p>
<p>knockoutjs是微软的大牛Steven Sanderson开发,深得MVVM的精髓,设计思路也很近似WPF,拥有一样属性绑定,
监控函数,依赖监控函数(我比较喜欢用emberjs的叫法,computed)与监控数组
虽然说是MVVM,应该有三个层,Model,View, ViewModel,在knockout中,Model只是作为ViewModel的定义而存在,
ViewModel创建后,Model就立即消弥,想重新得到纯数据的Model,可使用 ko.mapping.toJS还原。
ViewModel是复杂得难以想象的观察者,与View死死绑定在一起,而Model的原始数据则被打散锁到一个个闭包内,由可读写的
监控函数所操作。View主要是绑定。我们通过ko.applyBindings(myViewModel)方法,它就开始扫描DOM树,
然后将元素节点特定前缀的特性节点或注释节点的值抽取出来,转换为视图刷新函数,与ViewModel的监控函数在一起。
knockout以惊人的毅力抛弃jQuery自己对抗IE6的疑难杂症并成功了,而且是三千多行代码。knockout还一个监控数组,
我们只需要调用数组的splice, push, pop, sort等方法就能让对应的DOM列表发生变化,是“操作数据即操作DOM”理念的先行者。
它用注释节点进行绑定,就不会让页面在JS加载前出现乱码一样的情况。最赞的是,它操作一组节点,会根据Levenshtein Distance算法进行,将元素的创建,删除,移动等步骤减到最少。
不过,knockout的缺点也很明显,源码晦涩,
绑定冗长,操作数据时并不是按原Model样子操作——因为ViewModel都把它们转换为监控函数与监控数组。</p>
<p>emberjs是jQuery, rails,Sproutecore,Merb,Handlebars这几个著名框架的核心成员Yehuda Katz创建,它是Sproutecore2.0,
它依赖于Handlebars模块库,能够实现比knockout更复杂更精细的动态绑定,这时整个页面就是一个模板。
动态刷新的颗粒度细化到每一个特性节点,文本节点。它基本上把后端除数据库外的所有东西都挪到前端。
服务端变成了一个仅用来分发JSON API的机制,有趣的界面工作都在客户端用JS来完整实现。Model类似于数据定义表的字段那样,允许你制定一些验证规则,
Controller与rails一样,拥有一模一样action,我们也在视图看到它们踪影。链接与点击等事件会被劫持action,中间可能会通过linkTo等方法跑到Router到再Controller了。
为了防止点击链接发生页面跳转,用到了HTML5的新History方法与hash重写技术。相对于knockout操作数据时还能链式化,emberjs则是被包裹到set, get这两个上帝方法中,
而它们内部用到的是Object.defineProperty或__defineGetter__,defineSetter,这些读写器作为双向依赖链的末梢,
能非常敏感就捕捉到Model的改动并通知另一端的观察者去。因为操作DOM的成本远远操作一千个JS纯对象要大得多,因此在这个过程,它有一个非常严密的比较新旧值的过程,
对大对象每一个属性的改动及对数组元素的位置都能查个透彻。emberjs是设计API的高手,无论是命名还是实现都比其他两个高出一大截。
但它现在还远远没完工,虽然可以投入使用,但文档又跟不上。这是完美主义者的弱点。</p>
<p>angularjs是google组织开发的,1.0后大改动,由MVC改为现在的MVW,其实与MVVM差不多。参考者为google的算法帝,写个编译器不算什么难事,因此里面有两个parser。
HTMLParser用于抽取指令及解析自定义标签,其实自定义标签也归为一个指令,而指令就是knockout, emberjs的绑定。
在angular中,指令分为四种形态,插值表达式,可以位于文本节点,注释节点,类名,特性节点中;以特殊前缀命名的特性节点;
以特殊结构作辨识的类名,不过这个已经渐渐淡出视野了;自定义标签。它们会编译成一个个链接函数,里面包含着节点,因此相当于其他框架所说的刷新函数。
但由于angularjs允许用户随意在视图定义对象,而这些对象在angularjs的世界中必定属于某个作用域,作用域又存在套嵌,因此必须等到整个DOM扫描完才能够判定。
作用域是一个奇特的东西,其当于Model与Controller的集合,用于圈定视图某个区域是由它负责。如果它处理不了,那么就由它上一级的父作用域出马。
angularjs在视图的戏份非常重,Model完全弱化为作用域某个属性,比如ng-model指的是某一个字段而不是一个对象。
插值表达式相当于knockoutjs的text 绑定,显然比后者用一个属性来绑定简单多了,
并且通过管道符风格的过滤器,我们可以更优雅地格式化输出。数据的验证,我们可以临时拉上ng-require绑定来处理,这时其他两个框架不敢想象的。
angularjs拥有三者中最丰富的绑定器,能满足你做各种需求,并且提供了各种接口让你自定各种绑定,服务,视图助手。
JSParser主要用于分析各种绑定的值,与模型的字段相关联,完成双向绑定。
但官网看不到直接操作Model字段的例子,只教你用$watch监听,让框架去让调用绑定背后的处理函数。
它没有computed函数与监控数组,因此需要你自己去写指令模拟。从浏览器支持程度看,它支持到IE7,emberjs是IE8, knockoutjs是IE6。
从各项指标来看,它也是排第二。如果纯粹是MVVM,还是knockout做得最好。但由于knockout坚守几千行的代码现模,现在能与emberjs对抗的就是angular了。
两者都拥有路由,历史,验证,测试套装。angularjs的两个编译器其实能让它走得更远更好,但API设计太劣拙了(包括要兼容之前更笨的旧版),拉底了水准。
</p>
<p>而现在的avalon(v4)则是汲取上述三者优点搞鼓出来的。avalon之前三个版本主要是参考knockoutjs,
花了很长时间才弄懂如何智能收集依赖实现computed,以及与视图刷新函数的绑定。监控数组的实现甚至比knockoutjs更优雅。</p>
<p>avalon v4从emberjs借鉴了用Object.definePerperty监听数据改动的做法,并进一步使用VBScript让它在IE6也能运作。
Model被设计成数据描述对象,一经定义不能修改。这是出于建筑学的永恒之前——一座建筑的地基决定了它的底座,
底座决定了它的结构,结构决定了它的外观。 建筑的每一个阶段都比上一个阶段更固定,更难以改变。如果的确想改,也不是在原Model上改
而是使用CSS覆盖原则,用另一个对象的同名属性来覆盖。这是不是有点像子类改写父类呢。
但内部并不存在继承,而是使用angular类似的父子作用域。
</p>
<p>angular的{{}}插值表达式也抄过来,用于取替text binding,其实它就是text binding。不同的是刷新时,原来是对整个innerText进行修改,
现在只对某个文本节点的nodeValue进行重置了。管道符风格的过滤器也抄过来。
但传参时有点不同,使用小括号包起来,与JS传参一样,方便直接截取出来,然后eval。</p>
<p>computed监控属性也少不了,由于使用Object.definePerperty,变得更简单。监控数组也实现更优雅,knockout那个$index完全支持了。</p>

//http://jsfiddle.net/adamdbradley/Qdk5M/
<p>
<span class="stress">描述:</span>
</p>
<p>一个简单的事件绑定函数。</p>
<p>
<span class="stress">参数:</span>
</p>
<dl>
<dt>obj</dt>
<dd>必需。原生的事件发送器,如window,document与元素节点。</dd>
<dt>type</dt>
<dd>必需。String。事件类型,注意不要使用onXXX的格式。</dd>
<dt>fn</dt>
<dd>必需。Function。</dd>
<dt>phase</dt>
<dd>可选。Boolean。是否使用捕获,默认false,与不支持捕获的IE678保持一致。</dd>
</dl>
<p>
<span class="stress">返回值:</span>
</p>
<p>用户传入的fn</p>
<fieldset>
<legend>例子</legend>
<pre class="brush:javascript;gutter:false;toolbar:false">
var fn = $.bind(document, "click", function() {
alert("触发了点击事件并立即卸载它");
$.unbind(document, "click", fn)
});
</pre>
<button class="doc_btn" type="button">点我,执行代码</button>
</fieldset>
</article>
</body>

</html>
4 changes: 2 additions & 2 deletions doc/scripts/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ define("api", ["mass"], function() {
"Number.limit,Number.nearer,Number.round,Object.subset,Object.forEach,Object.map,Object.clone,Object.merge," +
"isPlainObject,isNative,isEmptyObject,isArrayLike,format,range,quote,dump,parseJS,parseJSON,parseXML,isArray,isFunction,each,map").match($.rword).sort(),
"class": ["factory"],
newland: [],
avalon: [],
node: ("fn.after,fn.afterTo,fn.append,fn.appendTo,fn.before,fn.beforeTo,fn.children," +
"fn.clone,fn.closest,fn.collect,fn.contents,fn.data,fn.each,fn.empty,fn.eq," +
"fn.even,fn.filter,fn.find,fn.first,fn.get,fn.gt,fn.has,fn.html,fn.index,fn.init," +
Expand All @@ -22,7 +22,7 @@ define("api", ["mass"], function() {
data: ["data", "parseData", "removeData", "mergeData"],
css: "fn.css, fn.width, fn.height, fn.innerWidth, fn.innerHeight, fn.outerWidth, fn.outerHeight, fn.offset, fn.position, fn.offsetParent, fn.scrollParent, fn.scrollTop, fn.scrollLeft, css".match($.rword).sort(),
attr: "fn.addClass, fn.hasClass, fn.removeClass, fn.toggleClass, fn.replaceClass, fn.val, fn.removeAttr, fn.removeProp, fn.attr, fn.prop, attr, prop".match($.rword).sort(),
event: ("fn.on,fn.bind,fn.off,fn.unbind,fn.toggle,fn.delegate,fn.live,Event,eventSupport" +
event: ("fn.on,fn.bind,fn.off,fn.unbind,fn.delegate,fn.live,Event,eventSupport" +
"fn.one,fn.undelegate,fn.die,fn.fire,fn.contextmenu,fn.click,fn.dblclick," +
"fn.mouseout,fn.mouseover,fn.mouseenter,fn.mouseleave,fn.mousemove," +
"fn.mousedown,fn.mouseup,fn.mousewheel,fn.abort,fn.error,fn.load,fn.unload," +
Expand Down
2 changes: 1 addition & 1 deletion lang.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ define("lang", Array.isArray ? ["mass"] : ["lang_fix"], function($) {
}
if (type === "Object") {
var i = obj.length;
return i >= 0 && i % 1 === 0; //非负整数
return (i >= 0) && (i % 1 === 0) && obj.hasOwnProperty("0"); //非负整数
}
return false;
},
Expand Down
4 changes: 3 additions & 1 deletion mass.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,9 @@
});
(function() {
var cur = getCurrentScript(true);
if(!cur){//处理window safari的Error没有stack的问题
cur = $.slice(document.scripts).pop().src;
}
var url = cur.replace(/[?#].*/, "");
kernel = $.config;
kernel.plugin = {};
Expand All @@ -324,7 +327,6 @@
break;
}
}

kernel.level = 9;
})();

Expand Down
Loading

0 comments on commit e2ae6c1

Please sign in to comment.