js模块化设计、内存管理相关
一、模块化设计
1、整体模块分析拆解
如果在项目初期阶段进行了良好的
模块化设计
,在项目开发中,需要重构,也是比较容易的,单个独立模块可以不用重构,直接使用,直接对主体框架进行重构就可以了,节省大量的时间、人力成本;如果没有进行良好的
模块化设计
,在需求发生变更,需要对功能做一点改动的,都需要很大的改动,如果迫不得已需要进行重构,相当于推掉重来,重新开发;
举例:直角坐标系
拆解过程:
1)拆解分离DOM
和canvas
子模块;
2)分析拆解DOM
模块,列出子模块,直至最小粒度;
3)分析拆解canvas
模块,列出其中的子模块,直至最小粒度;
4)分析哪些子模块是相同的,可以抽象出来的;
2、分析模块与同级模块、与子模块之间的联系,以及他们之间的对接方式
1)同级模块之间的耦合关系,尽可能确保每个模块都是独立的,同级模块之间零耦合;
2)同级模块之间如需要耦合,可以使用回调函数
的方式;
3)父模块与子模块之间耦合时,在子模块中定义好接口函数
,由父模块去调用;
4)子模块如需反向操作父模块,可以使用回调函数
的方式,一般不建议使用这么做,这样会造成父模块和子模块间双向耦合;
5)列出可能涉及到的接口函数
和回调函数
;
3、协同开发分工
1)按不同的模块进行分工开发,按照模块的复杂度、所需的开发人员进行分工,如:坐标系的DOM
比较简单,可由一人开发,canvas
比较复杂,如有两人同时开发,由于当中的子模块都是相对独立的模块,可以分配一个人去开发XY轴
模块,一个人去开发网格线
和象限
模块;
2)由主程搭设计建出主体框架,考虑好模块之间的耦合性;
3)模块开发时,严格按照设计,留好规定好的接口
,回调
;
4)进行开发时,如发现有些接口在设计时没有考虑到,需要别的模块开发人员提供,应及时沟通协调好,避免后面对接时出现问题;
5)主程在开发中,对代码的质量严格把控,注意模块间的耦合性、变量的定义使用(慎用全局的const
对象)、对象的引用(避免多出引用、尤其是强引用);
6)模块化
:按照OOP
的编程思想,在ES6
中可以使用import
和export
使得代码结构实现模块化
,使单个文件的代码得到简化,对于模块自身需实现的操作,都需在模块内部去实现,不要通过定义外部函数来操作,避免耦合;
7)和业务、模块无关或者没办法进行分装在内部的一些操作,如取值运算、对象销毁、与外部进行对接通信等可以使用import
和export
来进行代码简化;
二、内存管理
由于项目开始前期没有做好详细的设计,没做到
模块化
,在开发的时候不注意内存问题,没有做好该做的工作,造成内存没办法被管理到,进而出现内存泄露、程序崩溃、数据紊乱等问题,给整个项目带来灾难性的后果。在开发初期、中期,发现内存有问题,应及时修复,如果是因为设计的问题,没办法进行修复,需要重构,应当机立断、及时重构,因为问题是越积累越多的;
在项目的后期,尤其是已经进入到了测试阶段,出现内存问题,在原有的代码上没办法进行修复,解决问题,需要进行代码重构,这个代价是非常大的,需要付出很大的时间和人力成本,而且还不能确保最后重构的不会出现别的问题,尤其是在测试阶段时,这个给产品质量带来严重风险;
内存管理的良好实践
1、模块化设计
:保证每个定义的对象都是能被溯源的,避免由于对象被到处引用,在内存释放的时候,造成对象没办法得到释放;
2、事件的绑定、解绑:所有绑定的事件,在模块退出的时候,都需要解绑,如果事件一直都存在,相当于被引用,除了页面关闭,否则内存是没办法被浏览器回收的;
3、ES6
中,慎用const
对象,由于const
对象是无法再进行重新赋值的,在需要释放内存时,会造成内存泄露,当然对于一些常量,不需要进行更改而且需要在很多地方使用,可以使用const
;
4、使用export
的对象,在外部是无法重新赋值的,不建议初始化后的对象export
到外部去,使用模块化设计
的方式,进行分解抽象,一般都是可以解决问题的;
5、chrome
内存监测工具的使用:Timeline
和Profiles
。
三、jQuery相关
1、DOM操作
举例说明:
<div class="exam_wood" style="height: 100%">
<div class="com_layout layout_whiteboard_notime">
<div class="com_lay_contain">
<div class="com_lay_header">
<div class="com_lay_toptool">
<a href="javascript:void(0);" class="com_u_btn1 js-stop-sync-button" style="display: none;">结束任务</a><!--按钮不可点击添加类名:com_s_disabled-->
</div>
</div>
<div class="com_lay_mboard">
<div class="js-lego-view lego-view com_lay_board blocktower">
<div class="grid">
<div class="js-lego-view-bar">
<!-- placeholder -->
</div>
<div class="three_dimen js-lego-canvas">
<!-- canvas -->
<div class="canvasbox">
<div class="main">
<!-- canvas -->
</div>
</div>
<button class="btn_left rotation-control btn-arrow-left"></button>
<button class="btn_right rotation-control btn-arrow-right"></button>
<div class="btnbox clearfix">
<button class="btn btn-left-view">左面</button>
<button class="btn btn-front-view">正面</button>
<button class="btn btn-vertical-view">上面</button>
<button class="btn btnr btn-origin-view">原视角</button>
</div>
</div>
<div class="two_dimen">
<div class="js-lego-view-panel lego-view__panel">
<!-- placeholder -->
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
1)选择器selector
(1)由于我们开发学科模块的特殊性,同一个模块在一个页面中可能存在多个,所以在开发中不允许给DOM
节点使用id
,这边我们给DOM
添加class
,来进行操作。
例如:需要取得左面旋转按钮
//Bad
$('.btn-left-view')
//如果页面中有多个相同模块,那么这样选取出来的元素会有多个,会造成事件污染;
//Good
$(view).find('.btn-left-view')
//$(view)是模块的DOM,只要给内部元素没有绑定相同的class,这样可以确保选取出的元素是唯一的
(2)尽量使用find
方法查找子级元素,而不是Sizzle
引擎:
下面两行代码执行的结果是完全一样的,但是下面一句的效率要比上面一句的执行效率高。
现代浏览器(除IE6,IE7)都有QuerySelectorAll支持,能允许你像CSS选择器一样获取对象,而不需要用到jQuery中的Sizzle引擎。jQuery会在使用自己的引擎之前检查是否存在这个函数。
对于IE6/IE7,就需要jQuery使用Sizzle引擎,jQuery会把你的选择器转化成一个数组,并且通过从右往左来迭代匹配。通过正则表达式匹配页面每一个元素,所以你的可以尽量减少选择器的层级,尽可能的使用最右边的选择器,比如使用ID选择器等;这个规则和我们的css的查找规则是一至的,如果你要优化css选择器也要知道这个规则:从右往左来迭代匹配!
//Sizeele
$('#someDiv p.someClass').hide();
//find
$('#someDiv').find('p.someClass').hide();
(3)find
方法选取子元素,如果所需选取的元素在DOM
中,父级节点比较多,比较好的做法是先选取父节点,在选取子节点,可以加快jquery
的DOM
选取速度;
(4)尺度把握:保持代码简单,尽可能的使用find()
查找、尽可能使用最右边的选择器,比如ID等
2)DOM
事件
//事件绑定
$(selector).on('click',function);
$(selector).on('mousedown touchstart',function);
$(selector).on('mouseup touchend',function);
$(selector).on('mousemove',function);
//or
$(selector).bind('click',function);
$(selector).bind('mousedown touchstart',function)
$(selector).bind('mouseup touchend',function);
$(selector).bind('mousemove',function);
//事件解绑
$(selector).off('click',function);//function可选
$(selector).off('mousedown touchstart',function);//function可选
$(selector).off('mouseup touchend',function);//function可选
$(selector).off('mousemove',function);//function可选
//or
$(selector).unbind('click',function);//function可选
$(selector).on('mousedown touchstart',function);//function可选
$(selector).on('mouseup touchend',function);//function可选
$(selector).on('mousemove',function);//function可选
使用jquery
来进行事件绑定,如果元素不存在,不错报错,代码照样能运行,如果使用原生的DOM
事件,如果所取节点不存在,js代码保会报错,后面的代码就不能运行了。
3)动态添加DOM
节点,以及事件绑定
如需动态添加节点,并且给节点绑定事件,此时通过DOM
查找来进行绑定,此时DOM
节点是查找不到的,需在添加到DOM
中的时候,就对元素进行事件绑定;
举例:如需在DOM
中添加一个输入框,并且添加输入框文件change
事件
var inputElement = $('<input type="text" class='normal'>');
inputElement.change(function(e){
//change事件实现
});
$(parent).append(inputElement);
2、jQuery-ui
在我们的运行框架中都带有jquery-ui
这个js
库,里面的一些集成组件我们是可以直接使用的,可以避免写很多的事件:
如:拖动Draggable
、缩放Resizeable
、排序Sortable
等;
对于一些jquery-ui
的扩展控件需要jquery-ui.css
这个css
库支持,我们运行框架默认不加载,也不建议全部加载进去,避免造成和设计添加的css
冲突,如需某个控件,可以单独添加那一部分的css
。
如:滑块slider
、对话框Dialog
等
3、浏览器的渲染、重绘、重排 扩展学习
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。