一个项目是由许多人分工写的,因此必须要合理地拆散,于是有了模块化。体现在工作上,PM通常它这为某某版块,某某频道,某某页面。某一个模块,必须是包含其固有的数据,样式,HTML与处理逻辑。在jQuery时代,奉行的是“无侵入式javascript”,页面虽然是拆成一块块,但最后是通过PHP等后端模板合并起来,并且把第一屏的数据直接灌进去,接着是无尽的选择某些元素进行处理,选择某些元素进行处理。javascript里面是满屏的CSS表达式,如果不一一对着HTML页面,这是无法阅读的。换言之,jQuery很容易产生readyOnly的代码。

avalon是引入分层构架,视图就是视图,数据就是数据,JS里面是操作数据,不会再操作视图,泾渭分明。视图,换言之就是最初做好的那些HTML片段,只需要在里面添加上ms-controller指令(或叫绑定属性),指定其将要作用的ViewModel的ID,然后在它里面添加其他绑定就行了。数据,特指是ViewModel,avalon是通过define方法定义,目的是实现“操作数据即操作DOM”,从此我们再也用不上什么操作DOM的API,javascript代码量立即减少了一半以上,条理更清晰,更易维护。

ViewModel的定义是一个重头戏。在入门教程里,是这样定义的:

var model = avalon.define("test", function(vm) {
vm.firstName = "司徒"
vm.lastName = "正美"
vm.fullName = {//一个包含set或get的对象会被当成PropertyDescriptor,
set: function(val) {//里面必须用this指向scope,不能使用scope
var array = (val || "").split(" ");
this.firstName = array[0] || "";
this.lastName = array[1] || "";
},
get: function() {
return this.firstName + " " + this.lastName;
}
}
vm.arr = ["aaa", 'bbb', "ccc", "ddd"]
vm.selected = ["bbb", "ccc"]
vm.checkAllbool = vm.arr.length === vm.selected.length
vm.checkAll = function() {
if (this.checked) {
vm.selected = vm.arr
} else {
vm.selected.clear()
}
}
})
model.selected.$watch("length", function(n) {
model.checkAllbool = n === model.arr.size()
})

有两个参数,第一个定义ID,第二个是定义ViewModel本身的数据,它有什么监听属性啊,计算属性啊,一些特殊的指令啊,$watch回调啊用户需要区分vm与model的区别,有什么需要注意的地方(这些在入门教程都有介绍)。在1.3.3中,添加了现在这种新的定义方式,只要传入一个对象:

var model = avalon.define({
$id: "test",
firstName: "司徒",
lastName: "正美",
fullName: {//一个包含set或get的对象会被当成PropertyDescriptor,
set: function(val) {//里面必须用this指向scope,不能使用scope
var array = (val || "").split(" ");
this.firstName = array[0] || "";
this.lastName = array[1] || "";
},
get: function() {
return this.firstName + " " + this.lastName;
}
},
arr: ["aaa", 'bbb', "ccc", "ddd"],
selected: ["bbb", "ccc"],
checkAllbool: false,
checkAll: function() {
if (this.checked) {
model.selected = model.arr
} else {
model.selected.clear()
}
}
})
model.checkAllbool = model.arr.length === model.selected.length
model.selected.$watch("length", function(n) {
model.checkAllbool = n === model.arr.size()
})
  • 监控属性:就是改变了它会同步视图的属性;
  • 非监控属性:就是改变了它不会同步视图的属性,通常是以$开头,或放在$skipArray数组的属性(angular到1.3才引入单向数据绑定);
  • 计算属性:就是一个定义set, get方法的对象,是一种高级的监控属性;
  • 监控数组:就是一个数组,如果它没有以$开头,或名字没有放到$skipArray数组里,框架就会自动转换它为监控数组,当用户调用它的方法时,就会同步视图通常它是与ms-repeat、ms-each指令配合使用。

当我们将一个对象传进avalon.define方法,它将返回一个全新的对象,它添加了许多$方法与属性,并且原来的属性都变得非常奇怪,在控制台下可以看到它们都对着一个set方法一个get方法。ecma262 v5称之为访问器属性named accessor properties)。当然不同的人有不同的译法,大家想详细了解此属性的特性,可以阅读以下链接,这是avalon能让你修改属性就能同步视图的关键!

注意,我们所有定义的VM都存放在avalon.vmodels对象上。打开我们上一节写的项目,在firebug下输入avalon.vmodels可以查看到:

那么为了应用这些ViewModel,我们就需要用到ms-controllerms-importantms-skip这三个指令。ms-controller在页面上表现为一个特殊的属性,其属性值为ViewModel的$id,表示将在此元素或其子孙元素上圈定它的作用域范围,但如果这些HTML存在它没有的属性,它可以向上查找上一级的ViewModel的属性。换言之,ms-controller可以互相套嵌的。 ms-important的用法与ms-controller差不多,但它不会向上查找。ms-skip注明这块区域不应用任何的ViewModel的属性,它里面的任何指令(绑定属性)都会失效。因为{{}}也算一种指令,而任何指令在被扫描后都会被移除,如果我们想保留某个区域的{{}},就需要用到ms-skip。有关ms-controller, ms-important的详细用法可见这里

上面的ViewModel再配合一些HTML代码,就是实现一些用jQuery非常费劲才能实现的功能

<div ms-controller="test">
<p>First name: <input ms-duplex="firstName" /></p>
<p>Last name: <input ms-duplex="lastName" /></p>
<p>Hello, <input ms-duplex="fullName"></p>
<div>{{firstName +" | "+ lastName }}</div>
<ul>
<li><input type="checkbox" ms-click="checkAll" ms-checked="checkAllbool"/>全选</li>
<li ms-repeat="arr" ><input type="checkbox" ms-value="el" ms-duplex="selected"/>{{el}}</li>
</ul>
</div>

大家可以在这里看到实际运行效果。

再细说一下ViewModel(我们通常也简称为VM)的一些属性。

$id: VM的ID,方便在avalon.vmodels里查找到它,或用在ms-controller、ms-important上。

$events:里面存放着各种回调,它们是通过$watch方法添加的。

$watch:这是一个方法,有两个参数,第一个是VM中的某一个属性名,只能这个VM的直接子属性名,第二个是回调函数,当此属性发生改变时,就会执行此回调。回调里会依次传入它的新老属性值。

$unwatch:移除某个属性的回调。

$fire:手动触发此回调。

$accessors:放置与监听属性相连动的视图刷新函数,当我们改变某一属性时,框架就会在这里找到对应的视图刷新函数,传入当前值,实现对视图的同步。

$123323213:它的格式是$加上一串数字,它是用于放置监控数组的视图刷新函数,当我们调用监控数组的方法时,框架就此根据当前数组的个数与排列顺序,重新渲染对应的区域。它与$accessors一样,不开放给用户调用的。

$model:就是ViewModel的净化版,没有$XXX属性,访问器属性全部还原为普通属性,专门用于提交到后台用。当然我们提交后台,还需要用JSON.parse(JSON.stringify(VM.$model))处理一下,将里面的函数干掉。

现在说的还是基本用法,$watch、$unwatch、 $fire其实远远比你想象的强大,大家感兴趣的话,可以到这里了解其高级用法。

好了,我们把上面的代码放进上一节,修改aaa.js, aaa.html,感受一下一个复杂的ViewModel的应用吧。

有了ViewModel后,我们的代码就显得非常有内聚力,自己知道要作用于视图的哪一块区域,并且不用自己操心如此修改DOM,变成单纯的数据操作。

而数据操作是需要在页面定义一些指令(我们称之为绑定属性与插值表达式)。现在最简单的有两个,{{ prop }}是直接将属性输出到页面,如果它存在尖括号,会原样输出,不会转换为HTML标签。{{ prop | html}}则相反,比如这个属性的值为”xxxxerer”,那么里面就真会转为一个b标签。其实{{prop}},{{prop|html}}还有另一种写法, ms-text=”prop”, ms-html=”prop”。有关这些绑定的属性详细用法,我们下一节讲述。

本章节的代码可以从这里下载。

迷你MVVM框架 avalonjs 学习教程2、模块化、ViewModel、作用域的更多相关文章

  1. 迷你MVVM框架 avalonjs 学习教程19、avalon历史回顾

    avalon最早发布于2012.09.15,当时还只是mass Framework的一个模块,当时为了解决视图与JS代码的分耦,参考knockout开发出来. 它的依赖收集机制,视图扫描,绑定的命名d ...

  2. 迷你MVVM框架 avalonjs 学习教程18、一步步做一个todoMVC

    大凡出名的MVC,MVVM框架都有todo例子,我们也搞一下看看avalon是否这么便宜. 我们先从react的todo例子中扒一下HTML与CSS用用. <!doctype html> ...

  3. 迷你MVVM框架 avalonjs 学习教程3、绑定属性与扫描机制

    在MVVM框架中,你都会看到页面定了许多奇怪的属性,比如knockout的data-☆,angular的ng-☆,avalon的ms-☆,此外还有一些只写文本节点上的双花括号,它们统称为指令.ms-☆ ...

  4. 迷你MVVM框架 avalonjs 学习教程1、引入avalon

    avalon是国内最强大的MVVM框架,没有之一,虽然淘宝KISSY团队也搞了两个MVVM框架,但都无疾而终.其他的MVVM框架都没几个.也只有外国人与像我这样闲的架构师才有时间钻研这东西.我很早之前 ...

  5. 迷你MVVM框架 avalonjs 学习教程20、路由系统

    SPA的成功离开不这三个东西,分层架构,路由系统,储存系统.分层架构是我们组织复杂代码的关键,这里特指MVVM的avalon:路由系统是将多个页面压缩在一个页面的关键:储存系统特指本地储存,是安全保存 ...

  6. 迷你MVVM框架 avalonjs 学习教程16、过滤器

    avalon的过滤器是参考自angular与rivets.它也被称做管道文本过滤器,它的处理对象只能是文本(字符串),它只能用在文本绑定中,并且只能是双花括号形式.下面是各大家的过滤器比较: rive ...

  7. 迷你MVVM框架 avalonjs 学习教程11、循环操作

    avalon是通过ms-repeat实现对一组数据的批量输出.这一组数据可以是一个数组,也可以是一个哈希(或叫对象).我们先从数组说起吧. 第二节就说,凡是定义在VM中的数组,如果没有以$开头或者没放 ...

  8. 迷你MVVM框架 avalonjs 学习教程4、数据填充

    MVVM是前端的究极解决方案,你们可能用过jQuery,但那个写的代码不易维护:你们可以听过说requirejs与seajs,传说中的模块开发,加载器,但它们的最终目标是打包:你们可能听过unders ...

  9. 迷你MVVM框架 avalonjs 学习教程22、avalon性能大揭密

    avalon之所以能在页面处理1W个绑定(angular对应的数字是2000),出于两个重要设计--基于事件驱动的双向绑定链及智能CG回收机制. avalon的双向绑定链是通过Object.defin ...

随机推荐

  1. powerdesigner安装图解

  2. BZOJ2314 士兵的放置

    树形DP,恩然后就不会了... 先写了个错的离谱程序...果然WA了 然后开始乱搞,欸,对了! 令f[i], g[i], h[i]分别表示i号节点自己放士兵,被儿子上的士兵控制,不被儿子上的士兵控制但 ...

  3. get方法传送中文乱码解决方法

    找到tomcat配置文件 server.xml 找到<Connector port="8080" .......  />   (......为配置文件中原来内容) 在最 ...

  4. Alpha阶段第2周/共2周 Scrum立会报告+燃尽图 04

    作业要求[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2287] 版本控制:https://git.coding.net/liuyy08 ...

  5. @Transactional、Spring的声明式事务

    传送门 一.Spring的声明式事务 需要在xml文件中配置 <!--配置事务管理器类--> <bean id="transactionManager" clas ...

  6. 《DSP using MATLAB》Problem 2.9

    代码: %% ------------------------------------------------------------------------ %% Output Info about ...

  7. 【liunx】telnet命令

    telnet命令用于登录远程主机,对远程主机进行管理.telnet因为采用明文传送报文,安全性不好,很多Linux服务器都不开放telnet服务,而改用更安全的ssh方式了.但仍然有很多别的系统可能采 ...

  8. [CLPR] 用于加速训练神经网络的二阶方法

    本文翻译自: http://www.codeproject.com/Articles/16650/Neural-Network-for-Recognition-of-Handwritten-Digi ...

  9. linux Xinetd服务简介

    http://www.chuanke.com/course/72351180839190528______.html 1.什么是xinetdextended internet daemonxinetd ...

  10. Python VIL Service Bin

    #!/usr/bin/python #coding:UTF-8 import sys import re import getopt import md5 import os import subpr ...