avalon的ViewModel对象从其内部EventManager里继承了三个方法,$watch、$unwatch、$fire三个方法,它们就是我们本节的主题。

词如其名,非常直白,一看就知道做什么。我们先从$watch方法说起,它能监听当前的VM第一层的监控属性计算属性,如果某属性是一个对象,想监控其子孙属性,就需要定位到此对象上使用$watch回调了。$watch回调会默认传入先后两个属性值。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="avalon.js" ></script>
<script>
var model = avalon.define({
$id: "test",
aaa: "2",
bbb: "2",
$ccc: "1",//这是非监控属性
ddd: "1",//这是非监控属性
$skipArray: ["ddd"],
click: function(a) {
model[a] = new Date - 0
}
})
model.$watch("aaa", function(a, b) {
console.log("aaa", a, b)
})
model.$watch("bbb", function(a, b) {
console.log("bbb", a, b)
})
model.$watch("$ccc", function(a, b) {
console.log("$ccc", a, b)
})
model.$watch("ddd", function(a, b) {
console.log("ddd", a, b)
})
</script>
<style>
.ms-hover div:hover{
background:yellowgreen;
}
</style>
</head>
<body ms-controller="test" class='ms-hover'>
<div ms-click="click('aaa')">{{aaa}}</div>
<div ms-click="click('bbb')">{{bbb}}</div>
<div ms-click="click('$ccc')">{{$ccc}}</div>
<div ms-click="click('ddd')">{{ddd}}</div>
</body>
</html>

如果属性非常多,我们可以监听$all这个特殊的属性名来得知所有属性的变动状况。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="../avalon.js" ></script>
<script>
var model = avalon.define({
$id: "test",
aaa: "2",
bbb: "2",
$ccc: "1",
ddd: "1",
$skipArray: ["ddd"],
click: function(a) {
model[a] = new Date - 0
}
})
model.$watch("$all", function(name, a, b) {
console.log(name, a, b)
}) </script>
<style>
.ms-hover div:hover{
background:yellowgreen;
}
</style>
</head>
<body ms-controller="test" class='ms-hover'>
<div ms-click="click('aaa')">{{aaa}}</div>
<div ms-click="click('bbb')">{{bbb}}</div>
<div ms-click="click('$ccc')">{{$ccc}}</div>
<div ms-click="click('ddd')">{{ddd}}</div>
</body>
</html>

我们也可以用$fire更改属性值。这样就可以打破不能触发非监控属性的回调的藩蓠,但要注意死循环,需要自己比较新旧值是否真的发生改变才触发。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="../avalon.js" ></script>
<script>
var model = avalon.define({
$id: "test",
aaa: "2",
bbb: "2",
$ccc: "1",
ddd: "1",
$skipArray: ["ddd"],
click: function(a) {
var old = model[a]
model.$fire(a, new Date - 0, old)
}
})
model.$watch("$all", function(name, a, b) {
console.log(name, a, b)
}) </script>
<style>
.ms-hover div:hover{
background:yellowgreen;
}
</style>
</head>
<body ms-controller="test" class='ms-hover'>
<div ms-click="click('aaa')">{{aaa}}</div>
<div ms-click="click('bbb')">{{bbb}}</div>
<div ms-click="click('$ccc')">{{$ccc}}</div>
<div ms-click="click('ddd')">{{ddd}}</div>
</body>
</html>

注意,$watch回调里是用ecma262 v6 提供的新API Object.is做新旧值比较,它的功能与=== 差不多,但能对付NaN这个自己也不等于自己的怪胎。另,一个对象字面量即便外形看上去一致,也是一个新对象,不会等于原来的。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="avalon.js" ></script>
<script>
var model = avalon.define({
$id: "test",
aaa: "1111",
nan: NaN,
object: {a: 1, b: 2},
array: [1, 2],
ddd: "1",
$skipArray: ["ddd"],
click: function(a) {
if (a == "object") {
model[a] = {a: 1, b: 2}
} else if (a == "array") {
model[a] = [1, 2]
} else if (a == "nan") {
model[a] = NaN
} else {
model[a] = "1111"
}
}
})
model.$watch("$all", function(name, a, b) {
console.log(name, a, b)
}) </script>
<style>
.ms-hover div:hover{
background:yellowgreen;
}
</style>
</head>
<body ms-controller="test" class='ms-hover'>
<div ms-click="click('aaa')">{{aaa}}</div>
<div ms-click="click('nan')">{{nan}}</div>
<div ms-click="click('object')">
<div ms-repeat='object'>{{$key}}</div>
</div>
<div ms-click="click('array')">
<div ms-repeat='array'>{{el}}</div>
</div>
<div ms-click="click('ddd')">{{ddd}}</div>
</body>
</html>

对于数组,我们只能监听数组长度的变化,不能监听其内部是否发生变化。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="../avalon.js" ></script>
<script>
var model = avalon.define({
$id: "test",
array: [1, 2],
click: function(a) {
model.array.push(new Date - 0)
}
})
model.array.$watch("length", function( a, b) {
console.log(a, b)
}) </script>
<style>
.ms-hover div:hover{
background:yellowgreen;
}
</style>
</head>
<body ms-controller="test" class='ms-hover'>
<div ms-click="click('array')">
<div ms-repeat='array'>{{el}}</div>
</div>
</body>
</html>

如果你一定要监听数组每个元素的变化,可以使用1.3.4新添加的tick函数,这是一个心跳检测,只要函数返回false就会从检测列队中移除。由于是每30ms检测一次,非常耗性能,因此不用时记得移除。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="../avalon.js" ></script>
<script>
var ret
var model = avalon.define({
$id: "test",
array: [1, 2, 3, 4, 5, 6, 7, 8],
stop: function(){
ret = false
},
click: function(a) {
var index = Math.floor(Math.random() * 8)
model.array.set(index, new Date - 0)
}
})
var old = model.$model.array.concat()
avalon.tick(function() {
console.log("tick...")
var now = model.$model.array.concat()
for (var i = 0, n = now.length; i < n; i++) {
if (now[i] !== old[i]) {
console.log("第" + i + "个元素发生变化: " + old[i] + " --> " + now[i])
}
}
old = now
return ret
}) </script>
<style>
.ms-hover div:hover{
background:yellowgreen;
}
</style>
</head>
<body ms-controller="test" class='ms-hover'>
<div ms-click="click('array')">
<div ms-repeat='array'>{{el}}</div>
</div>
<button type='button' ms-click='stop'>移除此监听器</button>
</body>
</html>

稍微说一下 $unwatch的用法,这个不太常用。如果它传入两个参数,第一个为属性名,第二个为回调,那么就会移除此用户,如果只传入此属性名,则移除此属性的所有监听函数。如果什么也不传,那么就会临时中断此ViewModel的属性监听功能,所有$watch回调都不会触发。想恢复也很简单,调用$watch方法,也是什么也不传。

我们最后看一下1.3.2新增的跨模块通信功能,我们通过在$fire的第一个参数一些前缀,就能触发其他模块的属性回调。它们分别是”up!”, “down!”, “all!”。上与下是根据当前ViewModel所在ms-controller元素在DOM树位置决定的。

  • up!xxx, 向上冒泡
  • down!xxx, 向下捕获
  • all!xxx, 全局广播

<!DOCTYPE html>
<html>
<head>
<title>by 司徒正美</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="avalon.js"></script>
<script>
avalon.define("ancestor", function(vm) {
vm.aaa = '1111111111'
vm.$watch("aaa", function(v) {
avalon.log(v)
avalon.log("ancestor.aaa事件被触发了")
})
vm.click = function() {
avalon.log("向下广播")
vm.$fire("down!aaa", "capture")
}
})
avalon.define("parent", function(vm) {
vm.text = "222222222"
vm.aaa = '3333333333'
vm.$watch("aaa", function(v) {
avalon.log(v)
avalon.log("parent.aaa事件被触发了")
})
vm.click = function() {
console.log("全局扩播")
vm.$fire("all!aaa", "broadcast")
}
})
avalon.define("son", function(vm) {
vm.$watch("aaa", function(v) {
avalon.log(v)
avalon.log("son.aaa事件被触发了")
})
vm.click = function() {
console.log("向上冒泡")
vm.$fire("up!aaa", "bubble")
}
})
</script>
</head>
<body class="ms-controller" ms-controller="ancestor">
<h3>avalon vm.$fire的升级版 </h3>
<button type="button" ms-click="click">
capture
</button>
<div ms-controller="parent">
<button type="button" ms-click="click">broadcast</button>
<div ms-controller="son">
<button type="button" ms-click="click">
bubble
</button>
</div>
</div>
</body>
</html>

迷你MVVM框架 avalonjs 学习教程15、属性监听与模块通信的更多相关文章

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

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

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

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

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

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

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

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

  5. 迷你MVVM框架 avalonjs 学习教程2、模块化、ViewModel、作用域

    一个项目是由许多人分工写的,因此必须要合理地拆散,于是有了模块化.体现在工作上,PM通常它这为某某版块,某某频道,某某页面.某一个模块,必须是包含其固有的数据,样式,HTML与处理逻辑.在jQuery ...

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

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

  7. 迷你MVVM框架 avalonjs 学习教程21、双向绑定链

    avalon的双向绑定机制,是通过一条依赖链实现.此依赖链最底层是监控属性.监控数组,中层是计算属性.监控函数,再上点是求值函数,最上层是视图刷新函数. 所谓计算属性,监控属性,监控函数属性,我们改变 ...

  8. 迷你MVVM框架 avalonjs 学习教程12、数据联动

    在许多表单应用,我们经常遇到点击一个复选框(或下拉框)会引发旁边的复选框(或下拉框)发生改变,这种联动效果用avalon来做是非常简单的.在avalon里,存在各种绑定回调与$watch回调,完全满足 ...

  9. 迷你MVVM框架 avalonjs 学习教程8、属性操作

    属性操作是DOM操作很大的一块,它包括类名操作,表单元素的value属性操作,元素固有属性的管理,元素自定义属性的管理,某些元素的一些布尔属性的操作.大多数情况下,元素属性的值是字符串类型,我们称之为 ...

随机推荐

  1. win10禁用自动更新服务

    win10禁用自动更新服务 按Win+R,打开运行,输入"services.msc"打开服务: 找到"Windows Update",选择属性,修改为禁用即可: ...

  2. POJ2299逆序对模板(树状数组)

    题目:http://poj.org/problem?id=2299 只能相邻两个交换,所以交换一次只会减少一个逆序对.所以交换次数就是逆序对数. ps:原来树状数组还可以记录后边lowbit位的部分和 ...

  3. [转]csharp:Microsoft.Ink 手写识别(HandWriting Recognition)

    原贴:http://www.cnblogs.com/geovindu/p/3702427.html 下載: //Microsoft Windows XP Tablet PC Edition 2005 ...

  4. JavaScript模块化-RequireJs实现AMD规范的简单例子

    AMD规范简介 AMD(异步模块定义),是实现JavaScript模块化规范之一,它采用异步方式加载模块,模块的加载不影响后面语句的运行.require.js和curl.js都是实现AMD规范的优秀加 ...

  5. DHTMLX学习总结

    1.布局 Lyout = new dhtmlXLayoutObject(document.body, "2U"); 2.Grid grid.setHeader("#mas ...

  6. linux下maven项目clean失败

    今天在linux下创建了一个项目自动化发布的脚本,在执行到 mvn clean package -Dmaven.test.skip=true 的时候,项目clean失败 查下下度娘,windows下导 ...

  7. 1041 Be Unique (20 分)

    1041 Be Unique (20 分) Being unique is so important to people on Mars that even their lottery is desi ...

  8. [UE4]事件处理(Handling Events)和委托(Delegate)代码示例(一)

    1. 通过重写虚函数来处理事件 MyTriggerVolume.h 自定义一个Actor类,添加一个 Box 组件作为触发区域,然后通过重写虚函数——NotifyActorBeginOverlap, ...

  9. Log4net详细说明(全)

    转自:http://www.cnblogs.com/zhangchenliang/p/4546352.htmlhttp://www.cnblogs.com/zhangchenliang/p/45463 ...

  10. HFDS核心技术

    HDFS 设计的前提与目标 HDFS体系结构1 HDFS体系结构2 HDFS特性与优点 高容错性保障机制 HDFS不适合的场景 HDFS2.0的新特征 HA-QJM Federation 快照 异构层 ...