Ztree是一个比较优秀的树形展示组件,对于这个组件核心代码只有2000行不到,但是其功能却十分的强大,而且这个组件支持插件方式的扩展支持也是很不错的,
这可以让程序员围绕树的结构开发出更多的功能,比如check,edit两个功能就是以插件的方式提供,Ztree组件的代码整体结构有自身的一些特点,了解这些特征可以
使我们更好的使用这个组件.
在代码的内部主要分为以下几个部分:
1.常量定义对象(_consts);
2.组件配置对象(_setting);
3.生命周期回调方法(_initRoot,_initCache,_bindEvent...共11个分类);
4.代码主体对象(_z),这个对象按类别分为(_data,_event,_handler,_tools,_view)五个类别;
A._data,主要是数据节点的增,删,改,查,以及生命周期对象的管理;
B._event,主要是事件的绑定与代理,涉及生命周期方法_bindEvent,_unbindEvent;
C._handler,相关事件的处理器,主要与周期方法_eventProxy有关系,主要由event.bindTree,event.unbindTree调用;
D._tools,主要是一些工具方法;
E._view,视图相关的操作,涉及到几个生命周期_initNode,_beforeA,_innerBeforeA,_innerAfterA,_afterA
5.定义组件对象$.fn.zTree或是扩展$.fn.zTree
关于组件的事件设计相对比较复杂,主要分为两个体系:事件与回调,两者有相似之处但是并不相同,回调的入口主要是由event.bindTree
发起发起完成,本身实现采用的是jquery的监听实现,这个过程相对比较复杂event.bindTree使用event.proxy接管所有默认事件处理,
默认事件主要包括(selectstart,click,dblclick,mouseover,mouseout,mousedown,mouseup,contextmenu),事件处理过程如下:
event.proxy->event.doProxy->_eventProxy->handler(一系列内部处理器)->callback(一系列外部回调)
可以看到整个过程比较曲折其中_eventProxy是一个比较关键的代理处理器它会将标准事件通过target分析更加细化事件,并调用相应的
内部处理器(handler),在内部处理器中进一步细化回调callback,其实callback与handler性质是一样的,他们最大区别是在回调参数
callback回调参数更具有业务性,更像接口层,handler的回调参数更具备抽象性适合统一抽象.还有一部分回调是通过直接的事件绑定实现
而不是通过代理模式来完成回调,整体来说回调接口过程环节控制更加细腻,事件触发更加粗糙一些.
通过代理方式完成的回调接口主要有:
beforeCollapse,beforeExpand,beforeClick,beforeMouseDown,onMouseDown,beforeMouseUp,onMouseUp,
beforeDblClick,onDblClick,beforeRightClick,onRightClick.
通过事件绑定方式完成回调接口主要有:
onNodeCreated,onClick,onExpand,onCollapse,onAsyncSuccess,onAsyncError,onRemove,onSelected,onUnSelected
通常情况下设计人员更喜欢回调方法,因为回调往往就是足够的,时间绑定虽然比较自由但是一般情况下没有必要
_eventProxy通常来说是可以在插件开发中扩展的,但是有些局限性,这种模式比较适合围绕树的节点进行扩展的插件,对于在树的外围进行
扩展的插件似乎不是很适合,树的外围插件似乎更适合使用_bindEvent机制进行扩展
ztree组件有一个比较大的特点就是组件本身没有定义构造器,$.fn.zTree不是一个构造器而是一个普通对象,有点类似于一个名称空间,此处我认为定义
一个名称空间是可以的,但是没有必要定义在$.fn上,直接定义在jquery的静态成员上就可以($.zTree).这个名称空间主要挂接如下的对象:
1.consts常量;
2._z代码主体对象;
3.生命周期方法(init,destroy,getZTreeObj)
其实对于程序员使用方法往往只是三个生命周期方法:创建初始化(init),中途获取(getZtreeObj),用完销毁(destroy),这里需要主要说一下getZTreeObj方法
这个方法实际委托的是_z对象的_data对象的getZTreeTools方法,返回的对象是zTreeTools对象,这个对象是在init方法内部创建的一个工具对象,主要是树组件的常用
操作方法,主要是节点的增,删,改,查,刷新,选择,取消,展开等.
以上的就是ztree组件的基本结构,
data.initRoot(setting)->data.initCache(setting)->event.unbindTree(setting)-event.bindTree(setting)
->event.unbindEvent(setting)->event.bindEvent(setting)->data.setZTreeTools(setting, zTreeTools)
初始化的流程性生命周期:_initRoot,_initCache,_eventProxy,_unbindEvent,_bindEvent,_zTreeTools
视图创建过程中事件性生命周期:_initNode,_beforeA,_innerBeforeA,_innerAfterA,_afterA,主要方便插件扩展时,追加定制的dom结构
其中_initRoot,_initCache,_zTreeTools在data对象中触发,_eventProxy,_unbindEvent,_bindEvent在event对象中触发
_initNode,_beforeA,_innerBeforeA,_innerAfterA,_afterA在view的appendNodes中触发
关于ztree的扩展机制,有点像工业化的生产,定义结构基本还是与组件本身比较类似,定义自己的_consts,_setting,
_initRoot,_initCache,_bindEvent..._data,_event,_handler,_tools,_view等对象然后通过两个extend:
$.extend(true, $.fn.zTree.consts, _consts);
$.extend(true, $.fn.zTree._z, _z);
完成扩展,因此关于_consts,_data,_event,_handler,_tools,_view这些对象要小心,不要把核心中的方法覆盖了,我认为这也是
这种扩展方式不完善的地方,当然作者也考虑到了这一点,如果确实要覆盖可以采用在继承之后再覆盖方式例如:
var _createNodes = view.createNodes;
view.createNodes = function(setting, level, nodes, parentNode, index) {
//自己额外的逻辑
if (_createNodes) _createNodes.apply(view, arguments);
//自己额外的逻辑
};
这种方式可以解决方法覆盖的问题,但是不能解决覆盖误操作的问题,你可能覆盖核心方法,或覆盖其它插件的方法,或是被其它插件覆盖你自己
插件的方法,因此这种机制不适合大众开发,所有的插件都必须自己掌控才可以
(function($,undefined){
if(!$.fn.zTree){ //只有存在Ztree组件才进行配置扩展
return;
}
$.xTree = $.fn.zTree; //创建快捷方式
$.fn.xtree = function(zSetting, zNodes){
return $.xTree.init($(this),zSetting, zNodes);
};
//常量定义
var _consts = {
event: {},
id: {}
},
//配置定义
_setting = {
data: {key: {}},
callback: {}
},
//生命方法
_initRoot = function (setting) {},
_initCache = function(treeId) {},
_eventProxy = function(e) {},
_unbindEvent = function(setting) {},
_bindEvent = function(setting) {},
_zTreeTools = function(setting, zTreeTools) {},
_initNode = function(setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) {},
_beforeA = function(setting, node, html) {},
_innerBeforeA = function(setting, node, array) {},
_innerAfterA = function(setting, node, array) {},
_afterA = function(setting, node, html) {},
_data = {
//操作数据的方法
},
_event = {
//事件代理方法
},
_handler = {
//事件处理方法
},
_tools = {
//工具方法
},
_view = {
//视图操作方法
},
_z = {
tools: _tools,
view: _view,
event: _event,
data: _data
};
//常量与业务主体扩展
$.extend(true, $.fn.zTree.consts, _consts);
$.extend(true, $.fn.zTree._z, _z);
//这种方式可以为_z中回调方法提供快捷访问方式,但是不能保证它们不被其他插件覆盖
var zt = $.fn.zTree,
tools = zt._z.tools,
consts = zt.consts,
view = zt._z.view,
data = zt._z.data,
event = zt._z.event,
$$ = tools.$;
//配置嵌入
data.exSetting(_setting);
//生命周期嵌入
data.addInitBind(_bindEvent);
data.addInitUnBind(_unbindEvent);
data.addInitCache(_initCache);
data.addInitNode(_initNode);
//data.addInitProxy(_eventProxy, true); //代理方法返回值有特别要求必须是一个proxyResult,具体参考代码
data.addInitRoot(_initRoot);
data.addBeforeA(_beforeA);
data.addAfterA(_afterA);
data.addInnerBeforeA(_innerBeforeA);
data.addInnerAfterA(_innerAfterA);
data.addZTreeTools(_zTreeTools);
//方法覆盖
var _createNodes = view.createNodes;
view.createNodes = function(setting, level, nodes, parentNode, index) {
if (_createNodes) _createNodes.apply(view, arguments);
};
})(jQuery);