前端神器avalonJS⼊门(⼀)
avalonJS是司徒正美开发和维护的前端mvvm框架,可以轻松实现数据的隔离和双向绑定,相⽐angularJS等前端框架它有如下优势:
1.压缩后仅有60多kb,⽽angular的min版是100多kb;
2.兼容IE6+,符合天朝市场需求;
3.效率更⾼,跑起来⽐angular和knockout都要更快,在移动端上该优势会更⼤(avalon有移动端专版的dern.js)。关于其性能更详细的介绍可以看;
4.涵盖了angular的⼤部分功能,且实现⽅式更为便捷、上⼿更容易;
5.有配套的UI库(当然这个按需选择即可),由司徒正美及其“去哪⼉”团队维护,有相关的中⽂⽂档(下⽅会提到),除了在github提交issue,你也可以加⼊正美的Q79641290 来交流问题或提交bug。
(这位兄台,⾕歌送温暖,开门查⽔表)
然⽽avalon也有⾃⼰的劣势——知名度较低。不过毕竟国产的东西没经BAT推⼴,要像seaJS那样驰名中外倒是不容易。
相关中⽂⽂档
正美其实私下写了不少avalon的官⽅api和教程,⼤家可以访问如下地址:angular和angularjs
(下载最新的avalon以及实例(examples⽂件夹⾥),通过实例来掌握某些功能的实现是很好的学习途径)
(⽐较快捷的⼊门课程,只⽤了⼏篇⽂章来介绍了最常⽤的⼀些功能)
(正美的博⽂,篇幅较⼤,涵盖知识点很多,可以当作API来查阅,只是正美的博客排版真的。。。看起来略吃⼒),也可以在查看更规范的API。
(强烈推荐,⽤了20多篇⽂章较详细地、渐进地介绍avalon,必读的就是啦)
(推荐)
本系列初衷
虽然正美已经细⼼编写了不少中⽂⽂档,不过有的⽂章技术门槛有点⾼,不太适合初学者,另外作为avalon的⽤户,以⽤户的⾓度来较详细地介绍avalon或许会更合适些。
本系列相⽐正美的教程,会更侧重于“怎么⽤”,⽽⾮其机制或原理的介绍。
另外也希望本系列能为推⼴avalon出⼀份绵薄之⼒,希望能让更多的前端爱好者开始接触avalon,并喜欢上这个前端利器。
本系列技术需求
本系列除了avalonJS之外,还会搭配requireJS做辅助,特别是后⾯我们会使⽤avalon的路由系统,来做⼀个单页⾯站点(放到移动端就是SPA了),需要requireJS及其插件来按需加载脚本和样式⽂件,故建议查阅本系列的朋友要多多少少会⼀点requireJS的知识。
个⼈还是觉得avalon搭配requireJS的话,在前端可谓hold住全场了(咱忽略node及其框架...)~
开始
我们可以在获取最新版本的avalonJS,然后将其引⼊页⾯中(本章先不考虑搭配requireJS,仅仅先玩⼀玩、介绍下):
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>初玩阿⽡隆</title>
<script type="text/javascript" src="avalon.js"></script>
</head>
<body>
<div></div>
</body>
</html>
接着,类似于ng的“ng-controller”,avalon的控制域属性名叫做“ms-controller”,你可以把它当作⼀个,把它绑定到⼀个容器
后,avalon就能扫描和监听这个容器内的所有(绑定了avalon⽅法或带有插值表达式的)元素了。
我们来给div加上这个,并在⾥⾯写⼀个avalon插值表达式{{XXX}}:
<div ms-controller="wrap">{{a}}</div>
你现在运⾏它的话,还没有任何效果,因为我们还没有写脚本来让avalon⼯作起来。我们可以来这么⼀段简单的脚本:
<body>
<div ms-controller="wrap">{{a}}</div>
<script type="text/javascript">
var abc = avalon.define({  //abc是随便起的⼀个名字,⽤作该Model的载体
$id: "wrap",  //告诉avalon这个Model是作⽤于哪个ms-controller的
a: "你好啊"  //定义⼀个avalon对象属性“a”,其值是“你好啊”
});
avalon.scan(); //这句话可以不加,因为avalon有⾃⼰的DOMReady模块,会⾃动扫描全⽂。
</script>
</body>
在avalon中,我们⽤ avalon.define({ ... }) 的形式来定义⼀个Model实例(其参数可以看做⼀个avalon数据对象),其中的 $id 是内置属性,对应所要扫描和监控的控制域名。
我们还在内部定义了⼀个属性"a",故在对应的控制域(如这段代码对应的域是绑定ms-controller="wrap"的div标签)⾥,我们使⽤avalon插值表达式{{a}}的话,可以⾃动绑定其值“你好啊”。
注:最后⼀句 avalon.scan(); 可以不加上,不过后续我们会使⽤requireJS来配合使⽤avalon,届时会建议删掉avalon的DOMReady模块,故先养成加上scan的习惯。
上述代码运⾏效果如下:
当然,avalon有着更类似ng的写法:
avalon.define("wrap", function(vm) {
......
})
但个⼈觉得没必要,还是下⽅的写法来的简单(本系列后续的实例也将遵从该写法):
var vm = avalon.define({
$id:wrap,
......
})
下⾯这段代码可以帮你更好了解avalon的控制域:
<body>
<div ms-controller="wrap">{{a}}</div>
<div ms-controller="wrap2">
{{a}}
<span>{{b}}</span>
</div>
<script type="text/javascript">
var abc = avalon.define({
$id: "wrap",
a: "你好啊"
});
var def = avalon.define({
$id: "wrap2",
a: "⼤家好",
b: "哈哈哈"
});
avalon.scan();
</script>
</body>
执⾏效果如下:
两个作⽤域(ms-controller)之间可以互相访问彼此的数据,还记得我们给avalon.define的前⾯定义了⼀个载体么(var XX =
avalon.define),利⽤它就能轻松获取:
<body>
<div ms-controller="wrap">{{a}}</div>
<div ms-controller="wrap2">
{{a}}
<span>{{b}}</span>
</div>
<script type="text/javascript">
var abc = avalon.define({
$id: "wrap",
a: "你好啊"
});
var def = avalon.define({
$id: "wrap2",
a: "⼤家好",
b: abc.a  //获取第⼀个Model⾥的属性值
});
avalon.scan();
</script>
</body>
执⾏效果:
数据和视图同步
上⽅我们实现了⾮常简单的数据绑定,将⼀个avalon对象属性a绑定到DOM元素上。不过avalon更有趣和实⽤的地⽅是它实现了数据与视图的同步,说的简单点,我们⽤脚本修改了a的值,那么DOM上绑定的数据也会跟着改变(当然反过来也是⼀样的):
<body>
<div ms-controller="wrap">
<span>{{a}}</span>
<input ms-duplex="a" />
</div>
<script type="text/javascript">
var abc = avalon.define({
$id: "wrap",
a: "你好啊"
});
avalon.scan();
</script>
</body>
注意我们这⾥增加了⼀个 <input ms-duplex="a" /> ,其中的 ms-duplex 是avalon的双⼯绑定属性,它
除了负责将VM中对应的值(如本例是a)放到表单元素的value中,还对元素偷偷绑定⼀些事件,⽤于监听⽤户的输⼊从⽽⾃动刷新VM。
执⾏如下:
实例
利⽤avalon数据-视图同步的特性,我们可以更便捷地、更少代码地实现某些功能。举个例⼦,我们来实现⼀个选项卡的功能:
如上图的选项卡你会如何实现呢?可能你会写两个ul来对应下⽅两个选项卡列表,每个ul⾥都写上4个li(或者让后端⼈员通过后端框架来写loop,从⽽动态⽣成li),然后你再把第⼆个ul隐藏了,接着写个⽅法,让⿏标移到第⼆个选项卡标题时,第⼀个ul隐藏,第⼆个ul显⽰,对吧。
还有右上⾓的 “更多XX” 的连接,也可以通过隐藏-显⽰的⽅式来实现
你的DOM代码可能是这样的:
<div>
<span id="gg">公告</span><span id="bd">媒体报道</span>
<a id="more_gg" href="#!/gg">更多公告</a><a id="more_bd" href="#!/bd">更多报道</a>
<ul id="gg_list">
<li><a href="#!/gg/1" title="公告⽂章标题1">公告⽂章标题1</a></li>
<li><a href="#!/gg/2" title="公告⽂章标题2">公告⽂章标题2</a></li>
<li><a href="#!/gg/3" title="公告⽂章标题3">公告⽂章标题3</a></li>
<li><a href="#!/gg/4" title="公告⽂章标题4">公告⽂章标题4</a></li>
</ul>
<ul id="bd_list">
<li><a href="#!/bd/1" title="媒体报道⽂章标题1">媒体报道⽂章标题1</a></li>
<li><a href="#!/bd/2" title="媒体报道⽂章标题2">媒体报道⽂章标题2</a></li>
<li><a href="#!/bd/3" title="媒体报道⽂章标题3">媒体报道⽂章标题3</a></li>
<li><a href="#!/bd/4" title="媒体报道⽂章标题4">媒体报道⽂章标题4</a></li>
</ul>
</div>
但使⽤avalon的话,⼀切都更简单:
<div ms-controller="list">
<span ms-mouseover="changeUl(gg)">公告</span>
<span ms-mouseover="changeUL(bd)">媒体报道</span>
<a ms-href="'#!/'+ more_name">{{more_text}}</a>
<ul>
<li ms-repeat="infoList">
<a ms-href="'#!/'+ more_name + '/' + el.id" ms-title="el.title">{{el.title}}</a>
</li>
</ul>
</div>
⾸先它只有⼀个“更多XX”的<a>标签,⽽且只有⼀个<ul>,⽽且不存在任何后端标签的介⼊就能实现数据循环绑定。
它的优势在数据越多的时候会越明显(例如每个ul要显⽰100条li)。
我们来看alon的脚本应当怎么写:
<body>
<script type="text/javascript">
var gg=[{"id":"1","title":"公告⽂章标题1"},{"id":"2","title":"公告⽂章标题2"},{"id":"3","title":"公告⽂章标题3"},{"id":"4","title":"公告⽂章标题4"}];
var bd=[{"id":"1","title":"媒体报道⽂章标题1"},{"id":"2","title":"媒体报道⽂章标题2"},{"id":"3","title":"媒体
报道⽂章标题3"},{"id":"4","title":"媒体报道⽂章标题4"}];
</script>
<div ms-controller="list">
<span ms-mouseover="changeUl(1)">公告</span>
<span ms-mouseover="changeUl(0)">媒体报道</span>
<a ms-href="'#!/'+ more_name">{{more_text}}</a>
<ul>
<li ms-repeat="infoList">
<a ms-href="'#!/'+ more_name + '/' + el.id" ms-title="el.title">{{el.title}}</a>
</li>
</ul>
</div>
<script type="text/javascript">
var vm = avalon.define({
$id: "list",
more_name: "gg",
more_text: "更多公告",
gg:gg,
bd:bd,
infoList:gg,
changeUl:function(flag){
if(flag){  //⿏标移过“公告”选项卡头部
<_name = "gg";
<_text = "更多公告";
vm.infoList = vm.gg;
}else{  //⿏标移过“媒体报道”选项卡头部
<_name = "bd";
<_text = "更多报道";
vm.infoList = vm.bd;
}
}
});
avalon.scan();
</script>
</body>
执⾏效果:
我们来逐步分析下上⽅的代码。⾸先看第⼀段脚本:
<script type="text/javascript">
var gg=[{"id":"1","title":"公告⽂章标题1"},{"id":"2","title":"公告⽂章标题2"},{"id":"3","title":"公告⽂章标题3"},{"id":"4","title":"公告⽂章标题4"}];
var bd=[{"id":"1","title":"媒体报道⽂章标题1"},{"id":"2","title":"媒体报道⽂章标题2"},{"id":"3","title":"媒体报道⽂章标题3"},{"id":"4","title":"媒体报道⽂章标题4"}];
</script>
这⾥的 gg 表⽰“公告”的列表JSON数据,bd 则是“媒体报道”的列表JSON数据,你可以让后端的朋友直接在此处提供JSON数据过来。我们后续会利⽤avalon把这些数据绑定到页⾯视图上。
我们再看DOM结构:
<div ms-controller="list">
<span ms-mouseover="changeUl(1)">公告</span>
<span ms-mouseover="changeUl(0)">媒体报道</span>
<a ms-href="'#!/'+ more_name">{{more_text}}</a>
<ul>
<li ms-repeat="infoList">
<a ms-href="'#!/'+ more_name + '/' + el.id" ms-title="el.title">{{el.title}}</a>
</li>
</ul>
</div>
<span>中的 ms-mouseover 是avalon的“onmouseover”⽅法,其值 changeUl(X) 是我们在最后的avalon脚本中定义的⼀个事件⽅法,然后⽐如当⿏标移到“媒体报道”的span上,会触发绑定是 changeUl(0) 事件。
我们再看看 <a ms-href="'#!/'+ more_name">{{more_text}}</a>  这⾥的 ms-href ⾃然也是avalon中的“href”属性,可以植⼊avalon对象属性(如这⾥的more_name),也可以加上字符串(如这⾥的'#!/'),但要⽤引号括起来,不然会被当作avalon对象属性处理。
接着是最重要的部分:
<li ms-repeat="infoList">
<a ms-href="'#!/'+ more_name + '/' + el.id" ms-title="el.title">{{el.title}}</a>
</li>
我们使⽤了 ms-repeat="XX" 属性来绑定要重复显⽰的哈希数据,同时会⽣成⼀个代理VM对象,该代理对象拥有el,index,first, last,remove 等属性(点查看详细),其中我们⽤到的 el 表⽰指向当前的数据元素,从⽽可以通过 el.id ,el.title 来获取infoList数组对象的具体元素。
最后咱再看alon脚本:
<script type="text/javascript">
var vm = avalon.define({
$id: "list",
more_name: "gg",
more_text: "更多公告",
gg:gg,  //获取公告JSON数据
bd:bd,  //获取媒体报道JSON数据
infoList:gg,  //infoList缺省值为公告JSON数据
changeUl:function(flag){
if(flag){  //⿏标移过“公告”选项卡头部
<_name = "gg";
<_text = "更多公告";
vm.infoList = vm.gg;  //infoList变为公告JSON数据
}else{  //⿏标移过“媒体报道”选项卡头部
<_name = "bd";
<_text = "更多报道";
vm.infoList = vm.bd;  //infoList变为媒体报道JSON数据
}
}
});
avalon.scan();
</script>
这⾥要注意的是我们⽤了
gg:gg,  //获取公告JSON数据
bd:bd,  //获取媒体报道JSON数据
来获取和存储“公告/媒体报道”的JSON数据到avalon对象的属性中(左侧的gg和bd是avalon对象属性,右侧的gg和bd是全局变量),这样做的原因是后续的回调事件changeUl(flag)要通过参数来判断和修改vm.infoList的值,⽽其值应同为avalon对象属性。如果把代码改为这样会出错(刚⽤avalon的朋友可能就会这样写):
<script type="text/javascript">
var vm = avalon.define({
$id: "list",
more_name: "gg",
more_text: "更多公告",
infoList:gg,  //infoList缺省值为公告JSON数据
changeUl:function(flag){
if(flag){  //⿏标移过“公告”选项卡头部
<_name = "gg";
<_text = "更多公告";
vm.infoList = gg;
}else{  //⿏标移过“媒体报道”选项卡头部
<_name = "bd";
<_text = "更多报道";
vm.infoList = bd;
}
}
});
avalon.scan();
</script>
执⾏效果如下:
是的,⿏标第⼀次移上去的时候是⽆误的,但再移到其它选项卡的时候就不按常理出牌了,这是为什么?
这是因为当我们定义 “vm.infoList=gg” 时,vm.infoList ⾃然指向了 gg(注意gg是⼀个对象类型),若vm.infoList的值改变了,那么其指向的gg 对象的值⾃然也会跟着改变。你可以这样来调试:
var vm = avalon.define({
$id: "list",
more_name: "gg",
more_text: "更多公告",
infoList:gg,
changeUl:function(flag){
console.table(gg); //console出“公告”变量的数据信息
if(flag){
<_name = "gg";
<_text = "更多公告";
vm.infoList = gg;
}else{

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。