每个人都有一部手机,可以将其当做我们的前端模块,在这个模块的内部,我可以干各种事,玩游戏,看视频,听音乐等等,不会跟你的手机有任何关联,也就是解耦了。那么问题来了,是模块总是要通信的呀,该怎么通信呢?上图:
A手机想要和其他手机通信,是必须经过运营商的,所以我们的前端模块之间需要进行通信,也需要一个类似于运营商的东西,而该东西就是整个架构的核心,那就是事件管理者(EventManager)。通过事件管理者,当A模块需要调用B模块里面的render方法时,A模块将会通过事件管理者通知B模块执行它的render方法。反之,当B模块需要调用A模块的get方法时,也通过事件管理者,通知A模块执行它的get方法。这样A模块中不会存在B模块的字样,B模块中也不存在A模块的字样,它们中只会存在EventManager,一个为二者建立通信的通道。
这其实就是设计模式中的观察者模式,也称之为发布订阅模式。上面提到的事件已经不单是浏览器自带的那些事件(click,move等),它是有一定含义的自定义事件,可以起任何名称,比如:'dataChange'(数据改变事件),'render'(数据渲染事件),'clear'(页面清空事件)。模块内部,我们分层进行开发,采用mvc或者mvvm的开发方式。mvc只是一种设计思想,可以将model,view,controller分三个js文件开发,也可以在一个js文件中实现3种层次,另外controller过于臃肿的情况下,我们对其可以进行二次乃至三次分层,例如可以分成业务层,服务层等。我想说的是,mvc只是一种思想,它没有规定你必须几个文件,怎么书写,怎么具体分层,我们完全可以在符合思想的情况下为所欲为。结构如图:
上例子:
1、模块A:
1 T.ModuleA = { 2 init:function(){ 3 4 // 事件监听,监听清空事件 5 T.EventManager.addEvent('clear', this.clear, this); 6 }, 7 8 clear:function(id){ 9 document.getElementById(id).innerHTML = ''; 10 } 11 }
2、模块B:
1 T.ModuleB = { 2 init:function(){ 3 4 // 事件监听,监听清空事件 5 T.EventManager.addEvent('clear', this.clear, this); 6 }, 7 8 clear:function(id){ 9 document.getElementById(id).innerHTML = ''; 10 } 11 }
3、事件管理类:
1 T.EventManager = { 2 3 // 事件容器 4 eventContianer:{}, 5 6 /** 7 * 事件监听函数 8 * @param {string} evtName 事件名称 9 * @param {function} fn 函数引用 10 * @param {obj} ctx 上下文环境 11 */ 12 addEvent:function(evtName, fn, ctx){ 13 14 var obj = {'fn':fn, 'ctx':ctx}; 15 16 if(!this.eventContianer[evtName]){ 17 this.eventContianer[evtName] = []; 18 } 19 20 this.eventContianer[evtName].push(obj); 21 }, 22 23 /** 24 * 派发事件 25 * @param {string} evtName 需要触发的事件名称 26 * @param {array} args 需要传递给事件回调函数的参数 27 */ 28 dispatchEvent:function(evtName, args){ 29 var item, evts = this.eventContianer[evtName]; 30 31 // 事件没有注册,不派发 32 if(!evts){ 33 return; 34 } 35 36 for (var i = 0; i < evts.length; i++) { 37 var obj = evts[i]; 38 39 if (Array.isArray(args)) { 40 if(args[i]){ 41 item = args[i]; 42 } else { 43 item = null; 44 } 45 } else { 46 item = args; 47 } 48 49 obj.fn.call(obj.ctx, item); 50 } 51 } 52 }
4、页面展示
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="content-type" content="text/html; charset=utf-8"/> 5 <script type="text/javascript"> 6 window.T = window.T || {}; 7 </script> 8 <script type="text/javascript" src="EventManager.js"></script> 9 <script type="text/javascript" src="ModuleA.js"></script> 10 <script type="text/javascript" src="ModuleB.js"></script> 11 <style type="text/css"> 12 div{ 13 border: 1px solid black; 14 } 15 16 #moduleA{ 17 background-color: yellow; 18 } 19 20 #moduleB{ 21 background-color: green; 22 } 23 </style> 24 </head> 25 <body> 26 <div id="moduleA">我是模块A</div> 27 <div id="moduleB">我是模块B</div> 28 <input id="clear" type="button" value="清空"/> 29 <script type="text/javascript"> 30 T.ModuleA.init(); 31 T.ModuleB.init(); 32 document.getElementById('clear').onclick=function(){ 33 T.EventManager.dispatchEvent('clear', ['moduleA','moduleB']); 34 } 35 </script> 36 </body> 37 </html>
上面的例子,展示了事件广播的特性,模块A监听clear事件,模块B监听clear事件,事件管理器,触发clear事件,同时清空模块A的内容和模块B的内容。有点类似于运营商向每台手机发送消息。
这个demo只是一个简单的例子,旨在抛砖引玉,但核心思想就是这样的。具体的代码实现还是需要大家不断的完善,扩展,知识在于分享以及博众取长。