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. koa 知识点

    1. Koa 不支持 直接调用底层 res 进行响应处理.请避免使用以下 node 属性: res.statusCode res.writeHead() res.write() res.end() 2 ...

  2. 微信小程序开发(request请求后台获取不到data)

    1微信的request的post请求后台获取不到data(当初这个问题纠结了好久好久),原因是post传递的data是json格式而不是key,value的格式,所以获取不到相应的data就是post ...

  3. css-inline-block和float的布局二者择其一?

    几个月前,带着不甘和忐忑毅然决然的在亚马逊离职了,当时不知道对我来说是好是坏,现在看来,当初的选择还是蛮不错的.感觉在亚马逊的几个月貌似接触最多的就是wiki和tt了,怀着对技术热忱离开,拒绝了腾讯, ...

  4. Laravel日志查看器 -- log-viewer扩展

    1.修改laravel配置文件. config\app.php 'log'=>'daily' 2.在项目目录中composer命令安装扩展:composer require arcanedev/ ...

  5. antd在线换肤定制功能

    最近react项目,用的antd框架,然后看见他的antdPro例子里面有个定制功能很帅,老大说做,那就做吧,鼓捣了一晚终于实现了. 先看预览效果吧 css换肤 入行前端的时候经常看鱼哥(张鑫旭)的博 ...

  6. Hadoop专业解决方案-第1章 大数据和Hadoop生态圈

    一.前言: 非常感谢Hadoop专业解决方案群:313702010,兄弟们的大力支持,在此说一声辛苦了,经过两周的努力,已经有啦初步的成果,目前第1章 大数据和Hadoop生态圈小组已经翻译完成,在此 ...

  7. 【Hibernate学习笔记-4】在hibernate.cfg.xml中配置C3P0数据源

    jar包 hibernate.cfg.xml <?xml version="1.0" encoding="GBK"?> <!DOCTYPE h ...

  8. Java-Web中访问某个指定工程中的文件,报错后发现访问的文件是另一个工程里面的文件

    问题: 浏览器向我的bingou项目中的UserDaoImpl.java发送请求, myeclipse报错:空指针异常 点击报错行之后,错误给定位到了另一个项目中的的一个文件 解决: 原因是文件名错误 ...

  9. 杂项:BugFree

    ylbtech-杂项:BugFree BugFree是借鉴微软的研发流程和Bug管理理念,使用PHP+MySQL独立写出的一个Bug管理系统.简单实用.免费并且开放源代码(遵循GNU GPL). 命名 ...

  10. GROUP BY关键字与WITH ROLLUP一起使用

    1 GROUP_CONCAT mysql> SELECT student_name, ->     GROUP_CONCAT(test_score) ->     FROM stud ...