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

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

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width">
  6. <script src="avalon.js" ></script>
  7. <script>
  8. var model = avalon.define({
  9. $id: "test",
  10. aaa: "2",
  11. bbb: "2",
  12. $ccc: "1",//这是非监控属性
  13. ddd: "1",//这是非监控属性
  14. $skipArray: ["ddd"],
  15. click: function(a) {
  16. model[a] = new Date - 0
  17. }
  18. })
  19. model.$watch("aaa", function(a, b) {
  20. console.log("aaa", a, b)
  21. })
  22. model.$watch("bbb", function(a, b) {
  23. console.log("bbb", a, b)
  24. })
  25. model.$watch("$ccc", function(a, b) {
  26. console.log("$ccc", a, b)
  27. })
  28. model.$watch("ddd", function(a, b) {
  29. console.log("ddd", a, b)
  30. })
  31. </script>
  32. <style>
  33. .ms-hover div:hover{
  34. background:yellowgreen;
  35. }
  36. </style>
  37. </head>
  38. <body ms-controller="test" class='ms-hover'>
  39. <div ms-click="click('aaa')">{{aaa}}</div>
  40. <div ms-click="click('bbb')">{{bbb}}</div>
  41. <div ms-click="click('$ccc')">{{$ccc}}</div>
  42. <div ms-click="click('ddd')">{{ddd}}</div>
  43. </body>
  44. </html>

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

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width">
  6. <script src="../avalon.js" ></script>
  7. <script>
  8. var model = avalon.define({
  9. $id: "test",
  10. aaa: "2",
  11. bbb: "2",
  12. $ccc: "1",
  13. ddd: "1",
  14. $skipArray: ["ddd"],
  15. click: function(a) {
  16. model[a] = new Date - 0
  17. }
  18. })
  19. model.$watch("$all", function(name, a, b) {
  20. console.log(name, a, b)
  21. })
  22.  
  23. </script>
  24. <style>
  25. .ms-hover div:hover{
  26. background:yellowgreen;
  27. }
  28. </style>
  29. </head>
  30. <body ms-controller="test" class='ms-hover'>
  31. <div ms-click="click('aaa')">{{aaa}}</div>
  32. <div ms-click="click('bbb')">{{bbb}}</div>
  33. <div ms-click="click('$ccc')">{{$ccc}}</div>
  34. <div ms-click="click('ddd')">{{ddd}}</div>
  35. </body>
  36. </html>

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

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width">
  6. <script src="../avalon.js" ></script>
  7. <script>
  8. var model = avalon.define({
  9. $id: "test",
  10. aaa: "2",
  11. bbb: "2",
  12. $ccc: "1",
  13. ddd: "1",
  14. $skipArray: ["ddd"],
  15. click: function(a) {
  16. var old = model[a]
  17. model.$fire(a, new Date - 0, old)
  18. }
  19. })
  20. model.$watch("$all", function(name, a, b) {
  21. console.log(name, a, b)
  22. })
  23.  
  24. </script>
  25. <style>
  26. .ms-hover div:hover{
  27. background:yellowgreen;
  28. }
  29. </style>
  30. </head>
  31. <body ms-controller="test" class='ms-hover'>
  32. <div ms-click="click('aaa')">{{aaa}}</div>
  33. <div ms-click="click('bbb')">{{bbb}}</div>
  34. <div ms-click="click('$ccc')">{{$ccc}}</div>
  35. <div ms-click="click('ddd')">{{ddd}}</div>
  36. </body>
  37. </html>

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

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width">
  6. <script src="avalon.js" ></script>
  7. <script>
  8. var model = avalon.define({
  9. $id: "test",
  10. aaa: "1111",
  11. nan: NaN,
  12. object: {a: 1, b: 2},
  13. array: [1, 2],
  14. ddd: "1",
  15. $skipArray: ["ddd"],
  16. click: function(a) {
  17. if (a == "object") {
  18. model[a] = {a: 1, b: 2}
  19. } else if (a == "array") {
  20. model[a] = [1, 2]
  21. } else if (a == "nan") {
  22. model[a] = NaN
  23. } else {
  24. model[a] = "1111"
  25. }
  26. }
  27. })
  28. model.$watch("$all", function(name, a, b) {
  29. console.log(name, a, b)
  30. })
  31.  
  32. </script>
  33. <style>
  34. .ms-hover div:hover{
  35. background:yellowgreen;
  36. }
  37. </style>
  38. </head>
  39. <body ms-controller="test" class='ms-hover'>
  40. <div ms-click="click('aaa')">{{aaa}}</div>
  41. <div ms-click="click('nan')">{{nan}}</div>
  42. <div ms-click="click('object')">
  43. <div ms-repeat='object'>{{$key}}</div>
  44. </div>
  45. <div ms-click="click('array')">
  46. <div ms-repeat='array'>{{el}}</div>
  47. </div>
  48. <div ms-click="click('ddd')">{{ddd}}</div>
  49. </body>
  50. </html>

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

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width">
  6. <script src="../avalon.js" ></script>
  7. <script>
  8. var model = avalon.define({
  9. $id: "test",
  10. array: [1, 2],
  11. click: function(a) {
  12. model.array.push(new Date - 0)
  13. }
  14. })
  15. model.array.$watch("length", function( a, b) {
  16. console.log(a, b)
  17. })
  18.  
  19. </script>
  20. <style>
  21. .ms-hover div:hover{
  22. background:yellowgreen;
  23. }
  24. </style>
  25. </head>
  26. <body ms-controller="test" class='ms-hover'>
  27. <div ms-click="click('array')">
  28. <div ms-repeat='array'>{{el}}</div>
  29. </div>
  30. </body>
  31. </html>

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

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width">
  6. <script src="../avalon.js" ></script>
  7. <script>
  8. var ret
  9. var model = avalon.define({
  10. $id: "test",
  11. array: [1, 2, 3, 4, 5, 6, 7, 8],
  12. stop: function(){
  13. ret = false
  14. },
  15. click: function(a) {
  16. var index = Math.floor(Math.random() * 8)
  17. model.array.set(index, new Date - 0)
  18. }
  19. })
  20. var old = model.$model.array.concat()
  21. avalon.tick(function() {
  22. console.log("tick...")
  23. var now = model.$model.array.concat()
  24. for (var i = 0, n = now.length; i < n; i++) {
  25. if (now[i] !== old[i]) {
  26. console.log("第" + i + "个元素发生变化: " + old[i] + " --> " + now[i])
  27. }
  28. }
  29. old = now
  30. return ret
  31. })
  32.  
  33. </script>
  34. <style>
  35. .ms-hover div:hover{
  36. background:yellowgreen;
  37. }
  38. </style>
  39. </head>
  40. <body ms-controller="test" class='ms-hover'>
  41. <div ms-click="click('array')">
  42. <div ms-repeat='array'>{{el}}</div>
  43. </div>
  44. <button type='button' ms-click='stop'>移除此监听器</button>
  45. </body>
  46. </html>

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

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

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

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>by 司徒正美</title>
  5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  6. <script src="avalon.js"></script>
  7. <script>
  8. avalon.define("ancestor", function(vm) {
  9. vm.aaa = '1111111111'
  10. vm.$watch("aaa", function(v) {
  11. avalon.log(v)
  12. avalon.log("ancestor.aaa事件被触发了")
  13. })
  14. vm.click = function() {
  15. avalon.log("向下广播")
  16. vm.$fire("down!aaa", "capture")
  17. }
  18. })
  19. avalon.define("parent", function(vm) {
  20. vm.text = "222222222"
  21. vm.aaa = '3333333333'
  22. vm.$watch("aaa", function(v) {
  23. avalon.log(v)
  24. avalon.log("parent.aaa事件被触发了")
  25. })
  26. vm.click = function() {
  27. console.log("全局扩播")
  28. vm.$fire("all!aaa", "broadcast")
  29. }
  30. })
  31. avalon.define("son", function(vm) {
  32. vm.$watch("aaa", function(v) {
  33. avalon.log(v)
  34. avalon.log("son.aaa事件被触发了")
  35. })
  36. vm.click = function() {
  37. console.log("向上冒泡")
  38. vm.$fire("up!aaa", "bubble")
  39. }
  40. })
  41. </script>
  42. </head>
  43. <body class="ms-controller" ms-controller="ancestor">
  44. <h3>avalon vm.$fire的升级版 </h3>
  45. <button type="button" ms-click="click">
  46. capture
  47. </button>
  48. <div ms-controller="parent">
  49. <button type="button" ms-click="click">broadcast</button>
  50. <div ms-controller="son">
  51. <button type="button" ms-click="click">
  52. bubble
  53. </button>
  54. </div>
  55. </div>
  56. </body>
  57. </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. 各JAVA开发框架版本及对应信息

    日期:2017.05.22 当前最新 release 版本情况: 框架 最新GA(General Availability)版本 spring 4.3.8 spring boot 1.5.3 myba ...

  2. json包含单双引号问题解决方案

    解决方案:在后台处理 JSONArray.fromObject(list).toString() 转自明明如月小角落: 效果DEMO: JsonQuotesUtil.js /** * 解决json传输 ...

  3. 【jemter】HTTP请求参数化

    HTTP请求参数化:就是把URL的参数项做参数化处理 我们现在要对子猴博客来进行一番压力测试,压力测试对象为随机的几个网页链接,这几个链接是写在一个文本文件中的,在压力测试的时候会随机读取. 1.  ...

  4. pbuf类型和应用

    下面的讨论仅限于RAW API. 按存储方式分类 1. PBUF_RAM 从一般性的Heap中分配.可用空间大小受MEM_SIZE宏控制.可看作一般意义上的动态内存. 用途: a) 将应用层中的待发送 ...

  5. BASIC-19_蓝桥杯_完美的代价

    思路(贪心): 1.两边往中间逼近,步数少; 2.单个字符出现时只考虑移动到中间的步数,不做移动,因为这是最后进行,不影响结果; 示例代码: #include <stdio.h>#defi ...

  6. Date类型之继承方法

    ECMAScript中的Date类型是在早期Java中的java.util.Date类型基础上构建的.为此,Date类型使用自UTC(国际协调时间)1970年1月1日午夜零时开始经过的毫秒数来保存日期 ...

  7. Kibana安装及使用

    1.Kibana介绍Kibana是一个基于浏览器页面的Elasticsearch前端展示工具.Kibana全部使用HTML语言和Javascript编写的. 2.安装配置Kibana下载地址:http ...

  8. linux下一个网卡配置多个ip【虚拟ip】

    Linux下配置网卡ip别名何谓ip别名?用windows的话说,就是为一个网卡配置多个ip.什么场合增加ip别名能派上用场?布网需要.多ip访问测试.特定软件对多ip的需要...and so on. ...

  9. [UE4]添加蒙太奇动画

    选择蒙太奇所使用的骨骼

  10. SQL语句嵌套if

    在存储过程中我要实现一个IF的嵌套语句查询,类似与 if() {     if()      {         ......      }      else      {         .... ...