首页 > 代码库 > HT图形组件设计之道
HT图形组件设计之道
咱们号码大全经过定制了CPU和内存展示关键词挖掘工具界面,体会了HT for Web经过界说矢量完成图形制作与事务数据的代码解耦及绑定联动,这类事例后续文章还会持续以便咱们把握更多的矢量使用场景,本篇咱们先切换个论题,谈谈模型-视图-事情之间的联系。
图形组件计划架构上首要即是在计划Data模型,View视图和Event事情之间的联系,这些年业界逐步将各种GUI计划形式提炼成理论归类,MVC、MVP和MVVM的首要大类常被统称为MV*,有许多文章进行各种计划形式的界说和对比,本篇不计划深化翻开理论的讨论,不相同图形组件计划架构都会有许多区别,持续开展的组件本来每时每刻都在进行着各种计划上的改善,信任有许多不错的组件现已立异出了更多新的更有用的计划模型,只不过还未被提炼到理论高度进行归类让世人知晓,因而过细去界说啥是P,啥是VM,哪个功用大概写在哪个有些才算合理我觉得是没太大含义的,只需不断改善商品,团队能更好保护拓展,用户易学易用就够了,理论高度留给Martin Fowler这类神级大师去界说。
说到Martin Fowler由于他的《GUI Architectures》和《Presentation Model》是我较早见到将MVC和MVP理清的文章,从完成视点本来几十年前苹果用于开发Mac OS X的Cocoa Bindings技能已采用了相似的计划,并且Objective-C言语的Key-Value Coding和Key-Value Observing机制,加上XCode东西的可视化支撑,能够说多年来早已让很多开发者不知不觉在享用这些计划模型能带来的开发力。Java的Swing界面一向饱尝诟病,但本来很早就有JGoodies这样优异项目,Swing本就不算群众,知道JGoodies更是小众,而更少人知道JGoodies Binding这多年前就完成得十分不错的MVP架构封装,有爱好的读者可看看JGoodies这篇06年的PPT《Desktop Patterns and Data Binding》。
Adobe的Flex和微软的Silverlight/WPF本被业界寄予厚望,没想这哥俩如仓促过客被老东家扔掉了,但他们仍是推动了MVP和MVVM计划形式的遍及,如今HTML5范畴的KnockoutJS、Backbone.js、AngularJS、PureMVC、Ember.js等很多MV*结构假如雨后春笋般兴起,乃至需求有人专门保护个TodoMVC的网站来:Helping you select an MV* framework!
HT自身也是一套MV*的结构,但咱们训练客户时很少过细讨论计划形式,在我看来好的组件封装大概不必让用户纠结于你的计划形式,用户几个月不必你的结构后,仍然能疾速上手不必有一个重写学习的进程,这是咱们最求的抱负结构,从这个视点说当前很少有图形结构能让咱们满足,信任许多人有相似痛苦的阅历,一段时间不必某套结构后,要用时完全忘掉怎么下手,Swing内行不看老代码不知怎么对JTree和JTable增加数据,Flex内行一会儿想不起来invalidateProperties,invalidateSize和 invalidateDisplayList这几个自界说组件必把握函数的细节,SL/WPF内行想不起来界说一个DependencyProperty特点除了AffectsRenderer和AffectsMeasure还有多少要思考的要素,上段说到的成堆新式的HTML5界MV*结构,信任更少有人敢说娴熟通晓,你可能在某个项目中用了好几个月乃至一两年,但一段时间不必你很简单忘掉,因而对喊出通晓缺乏勇气了,我觉得这不是咱们不聪明不勤勉,而是当前的这些结构真还没做到满足好,咱们一向尽力让HT朝咱们觉得满足的方向开展,今后文章我再翻开讨论HT怎么计划让用户不健忘的API接口。
回到今日模型-视图-事情的论题,Data和View别离后必然需求有Event事情的监听和派发机制来建立起数据绑定,我控制欲对比强不是很喜欢AngularJS那种dirty checking的机制,有事情改变我期望立刻被告诉到,做我该做的处置,至于有人忧虑功用疑问那是多虑了,图形组件开展这么多年已堆集很多老练窍门来躲避事情的功用疑问。
功用疑问倒不必忧虑,究竟这方面任务大有些状况都是交由结构完成者去思考,但不需求用户深化知道结构的完成细节,并不意味着用户能够完全不联系根本架构头绪,结构使用者仍是有必要知道模型-视图-事情之间的引证相关联系,否则简单呈现内存走漏的疑问,曾经阅历过一个客户团队计划的客户端结构,可管理一切界面的窗口,成果呈现总是OOM的内存溢出,帮他们查看后发现,他们有个大局的WindowManager目标,在每个窗口创立时都会增加对窗口的引证,这样当然形似很强壮,大局都能够控制一切界面窗口,但由于绝大多数开发人员,不会在窗口封闭要毁掉时自动去删去大局WindowManager目标的引证,进而导致了一切窗口都能被大局目标引证到而无法废物收回,因而结构的使用者仍是有必要多结构的机制有所知道才干防止这类的内存走漏疑问。
许多状况下内存走漏不是长时间的运转也很难发觉,但关于HT的Graph3dView这种根据WebGL的3D组件疑问尤为显着,由于大有些浏览器对单个页面能运转的WebGL上下文是有约束的,例如PC上的chrome或firefox也就运转十五六个,手机平板等移动终端会更受限,因而假如呈现内存走漏老的上下文没封闭,逾越上限时就会呈现类型”Too many active WebGL contexts. Oldest context will be lost.”的反常。
以下我对《HT入门手册》的第一个比如做个拓展,对东西条增加了如下代码逻辑的三个按钮,第一个按钮一会儿创立了20个新的Tab页,每个Tab页包括一个Graph3dView组件,别的两个按钮完成删去有些页签的功用。
Js代码 保藏代码
{
label: ‘Create 20‘,
action: function(){
for(var i=0;i<20;i++){
var tab = new ht.Tab();
tab.setName(‘tab-‘+i);
tab.setClosable(true);
tabView.getTabModel().add(tab);
var g3d = new ht.graph3d.Graph3dView(dataModel);
g3d.name = ‘g3d-‘ + i;
window[‘g3d-‘ + i] = g3d;
tab.setView(g3d);
- indexRead arguments from command-line "http://www.shoudashou.com"
- indexRead arguments from command-line "http://www.4lunwen.cn"
- indexRead arguments from command-line "http://www.zx1234.cn"
- indexRead arguments from command-line "http://www.penbar.cn"
- indexRead arguments from command-line "http://www.whathappy.cn"
- indexRead arguments from command-line "http://www.lunjin.net"
- indexRead arguments from command-line "http://www.ssstyle.cn"
- indexRead arguments from command-line "http://www.91fish.cn"
- indexRead arguments from command-line "http://www.fanselang.com"
}
}
},
{
label: ‘Destroy 5‘,
action: function(){
var emptyModel = new ht.DataModel();
tabView.remove(‘tab-5‘);
window[‘g3d-5‘].setDataModel(emptyModel);
delete window[‘g3d-5‘];
this.disabled = true;
}
},
{
label: ‘Destroy 6-10‘,
action: function(){
for(var i=6; i<=10; i++){
tabView.remove(‘tab-‘ + i);
var emptyModel = new ht.DataModel();
window[‘g3d-‘ + i].setDataModel(emptyModel);
delete window[‘g3d-‘ + i];
}
this.disabled = true;
}
}
点击创立20个页签的按钮别离翻开页签以后系统的内存目标引证联系如下图所示:
Screen Shot 2014-08-15 at 9.46.17 PM
由于dataModel作为大局目标被window使用着,并且其他新创立的页签中的Graph3dView都绑定了该数据模型,结构使用者大概知道,各种组件都对dataModel数据模型增加了事情监听,本来数据模型并不知道各种View的存在,数据模型仅遵循有数据改变后将事情准确的派发给一切消费者,而这20个Graph3dView即是其间的消费者,而Graph3dView中每个有都有一个WebGL的context上下文,因而形成了一条从大局window到dataModel数据模型,再到Graph3dView组件,最后到WebGL上下文的引证联系网,这样天然假如咱们不自动断开这个联系,哪怕Tab页签被封闭毁掉,Graph3dView仍然还会存在系统内存的疑问(这个比如咱们为了测验便利本来还在window上直接引证了Tab和Graph3dView目标)。
Screen Shot 2014-08-15 at 10.12.59 PM
因而由以上视频你会发如今chrome下当点击到第16个包括Graph3dView的页签后就呈现了”Too many active WebGL contexts. Oldest context will be lost.”的反常,在WebGL中可经过对Canvas增加webglcontextlost的事情监听可判别自个的上下文被毁掉了,并可经过增加webglcontextrestored的事情监听在浏览器资本满足时从头进行康复。
在咱们这个事例中要让系统资本康复,咱们有必要让过多的Tab页签中的Graph3dView被完全收回,因而东西条上的别的两个按钮从代码逻辑可知,咱们将Graph3dView设置了一个新的空得DataModel数据模型,使其断开了和大局window.dataModel的引证,当然Tab页签也得删去,从以上视频中也能够看得出当咱们毁掉了有些Tab页签后就能得到webglcontextrestored的事情康复,因而第一个”HT for 3D Web”的页签阅历了webglcontextlost和webglcontextrestored的进程。
启动初始化时只要”HT for 3D Web”的第一个页签,因而经过Chrome的Debug Profiles可查看到ht.graph3d.Graph3dView的Objects Count项只要1,经过Profiles的Retainers咱们还能够明白的把握当前到达那些目标引证了Graph3dView目标:
Screen Shot 2014-08-15 at 10.11.31 PM
当点击构建20个页签按钮后,Profiles能看到Objects Count为21:
Screen Shot 2014-08-15 at 10.11.50 PM
当咱们点击两个删去按钮毁掉6个Tab页签后发现,Objects Count降低到了15:
Screen Shot 2014-08-15 at 10.12.23 PM
最后能够发现第一个HT for 3D Web的页签浴火重生了
Screen Shot 2014-08-15 at 10.13.47 PM
这个事例仅仅为了测验便利因而将dataModel目标作为大局变量,所以引发了一些列内存走漏的资本缺乏疑问,通常项目使用中不必的组件不需求思考这么杂乱,例如还需求断开dataModel引证这些过程,惯例使用场景中例如一个对话框翻开后,通常数据模型和视图组件都在这个对话框范围内彼此引证,只需保证不呈现上文说到的有大局引证能影响这个对话框内的某个目标,那么你在使用完该对话框后不需求做任何处置,那成堆的目标哪怕他们之间引证再杂乱乃至互相使用,横竖没有大局目标能够再引证到他们,他们通通都会被毁掉。
总结下本篇的两个观念:
1、再好的封装计划也需求使用者把握根本的架构头绪,就像再好的车你也得学会开学会根本的养护,啥都不学的话,再好的结构也会像好车相同被你开坏
2、不要惧怕MV*的事情和引证联系,理清事情机制和目标引证联系后,你能够准确掌控任何时刻的任何内部细节,这点首要针对计划结构者而言,使用者大概大胆的拥抱MV*的结构,功用和各种潜在的内存疑问放心的交给结构去处理