关于seajs模块间相互依赖调用的解耦问题
前端密集型项目中如果数据同步的量很大,往往需要对于多个组件进行数据同步,其中必会导致几个组件之间的互相依赖调用,在seajs中递归嵌套的调用组件会引起循环引用导致内存泄露。
比如有这样两个模块组件:
//@file : module/buddy/friend-in-group/friend-in-group.js define(function(require, exports, module){ var $ = require('lib/jquery.js'), _ = require('lib/underscore.js'), friend = require('module/buddy/add-friend/add-friend'); var _addFriend = function(){ // Some logic here }; var _removeFriend = function(uid){ // Some logic here }; exports.addFriend = function(uid){ // Some logic here _addFriend(uid,false); }; exports.removeFriend = function(uid){ // Some logic here _removeFriend(uid,false); }; exports.init = function(){ // Some logic here friend.add(); } });
//@file : module/buddy/add-friend/add-friend.js define(function(require, exports, module){ var $ = require('lib/jquery.js'), _ = require('lib/underscore.js'), fig = require('module/buddy/friend-in-group/friend-in-group'); var itemClickAction = function(){ var increase; // Some logic here if(increase === 1){ fig.addFriend(uid,gid); }else{ fig.removeFriend(uid,gid); } }; exports.add = function(){ // Some logic here itemClickAction(); } });
这样两个模块组件,friend-in-group组件 和 add-friend组件,friend-in-group组件由于一些UI需求需要调用add-friend组件,而friend-in-group组件暴露的两个方法addFriend和removeFriend是用于其他模块同步数据时用的,正好add-friend其中当执行itemClickAction方法时由于用户的动作会产生数据同步操作需要调用friend-in-group暴露的这两个方法。这样就产生了闭包的循环调用,seajs处会直接报错。如何对这两个模块进行解耦呢?如果是单例模式的模块其实可以直接采用拟事件注册的方法在内部解决,但是比如说add-friend这个模块是非单例且重用度很大呢?这个问题在项目中的确是个麻烦事。
参考了backbone模块间通信模式后可以有中间件进行方法注入负责模块间通信。引入中间件:
define(function(require, exports, module){ var $ = require('lib/jquery'); var _ = require('lib/underscore'); var events = {}; exports.subscribe = function(type,fn,arg){ if(!events[type]){ events[type] = {}; } events[type] = { fn:fn, arg:arg }; }; exports.unsubscribe = function(type){ if(!events[type] || !type ){ return false; } if(!!events[type]){ events[type] = {}; return true; } return false; }; exports.fire = function(type,data){ if(events[type]){ events[type].fn(data,events[type].arg); } }; });
这个模块首先是不会引入要解耦两方模块的任何一方,降低了耦合性。主要的是一个暴露的注册方法,和一个触发器方法,这样可以在friend-in-group方法中向中间件注入addFriend和removeFriend方法再在add-friend组件中进行触发,其中并不会形成递归的循环调用。
//@file : module/buddy/friend-in-group/friend-in-group.js define(function(require, exports, module){ var $ = require('lib/jquery.js'), _ = require('lib/underscore.js'), fmw = require('module/buddy/friend-in-group/friend-middleware'), friend = require('module/buddy/add-friend/add-friend'); var _addFriend = function(){ // Some logic here }; var _removeFriend = function(uid){ // Some logic here }; var subscribeEvent = function(){ fmw.subscribe('add',function(data,arg){ // Some logic here _addFriend(uid,false); },[currentGroupId]); fmw.subscribe('remove',function(data,arg){ // Some logic here _removeFriend(uid); },[currentGroupId]); } exports.init = function(){ // Some logic here friend.add(); } });
//@file : module/buddy/friend-in-group/friend-in-group.js define(function(require, exports, module){ var $ = require('lib/jquery.js'), _ = require('lib/underscore.js'), fmw = require('module/buddy/friend-in-group/friend-middleware'), friend = require('module/buddy/add-friend/add-friend'); var _addFriend = function(){ // Some logic here }; var _removeFriend = function(uid){ // Some logic here }; var subscribeEvent = function(){ fmw.subscribe('add',function(data,arg){ // Some logic here _addFriend(uid,false); },[currentGroupId]); fmw.subscribe('remove',function(data,arg){ // Some logic here _removeFriend(uid); },[currentGroupId]); } exports.init = function(){ subscribeEvent(); friend.add(); } });
如上面的代码便可以进行方法的注入与方法的触发,在friend-in-group中注册add和remove的方法事件,在add-friend中进行方法事件的触发。在subscribeEvent方法中将匿名方法传递到了中间件模块中,这个匿名函数在arg中保存了注册组件方的传入数据,而参数data
则是触发模块方的传入参数,在注册模块方引用该模块的私有函数,相当于将这个匿名函数的闭包作用范围扩展到了注册模块内部。这样在触发事件时便可保留触发时注册模块的一切被外引用状态。起到了解耦的效果。