一、模块化设计

1、整体模块分析拆解

如果在项目初期阶段进行了良好的模块化设计,在项目开发中,需要重构,也是比较容易的,单个独立模块可以不用重构,直接使用,直接对主体框架进行重构就可以了,节省大量的时间、人力成本;

如果没有进行良好的模块化设计,在需求发生变更,需要对功能做一点改动的,都需要很大的改动,如果迫不得已需要进行重构,相当于推掉重来,重新开发;

举例:直角坐标系

cordinate.jpg

拆解过程:
1)拆解分离DOMcanvas子模块;
2)分析拆解DOM模块,列出子模块,直至最小粒度;
3)分析拆解canvas模块,列出其中的子模块,直至最小粒度;
4)分析哪些子模块是相同的,可以抽象出来的;

2、分析模块与同级模块、与子模块之间的联系,以及他们之间的对接方式

1)同级模块之间的耦合关系,尽可能确保每个模块都是独立的,同级模块之间零耦合;
2)同级模块之间如需要耦合,可以使用回调函数的方式;
3)父模块与子模块之间耦合时,在子模块中定义好接口函数,由父模块去调用;
4)子模块如需反向操作父模块,可以使用回调函数的方式,一般不建议使用这么做,这样会造成父模块和子模块间双向耦合;
5)列出可能涉及到的接口函数回调函数

3、协同开发分工

1)按不同的模块进行分工开发,按照模块的复杂度、所需的开发人员进行分工,如:坐标系的DOM比较简单,可由一人开发,canvas比较复杂,如有两人同时开发,由于当中的子模块都是相对独立的模块,可以分配一个人去开发XY轴模块,一个人去开发网格线象限模块;
2)由主程搭设计建出主体框架,考虑好模块之间的耦合性;
3)模块开发时,严格按照设计,留好规定好的接口回调
4)进行开发时,如发现有些接口在设计时没有考虑到,需要别的模块开发人员提供,应及时沟通协调好,避免后面对接时出现问题;
5)主程在开发中,对代码的质量严格把控,注意模块间的耦合性、变量的定义使用(慎用全局的const对象)、对象的引用(避免多出引用、尤其是强引用);
6)模块化:按照OOP的编程思想,在ES6中可以使用importexport使得代码结构实现模块化,使单个文件的代码得到简化,对于模块自身需实现的操作,都需在模块内部去实现,不要通过定义外部函数来操作,避免耦合;
7)和业务、模块无关或者没办法进行分装在内部的一些操作,如取值运算、对象销毁、与外部进行对接通信等可以使用importexport来进行代码简化;

二、内存管理

由于项目开始前期没有做好详细的设计,没做到模块化,在开发的时候不注意内存问题,没有做好该做的工作,造成内存没办法被管理到,进而出现内存泄露、程序崩溃、数据紊乱等问题,给整个项目带来灾难性的后果。

在开发初期、中期,发现内存有问题,应及时修复,如果是因为设计的问题,没办法进行修复,需要重构,应当机立断、及时重构,因为问题是越积累越多的;

在项目的后期,尤其是已经进入到了测试阶段,出现内存问题,在原有的代码上没办法进行修复,解决问题,需要进行代码重构,这个代价是非常大的,需要付出很大的时间和人力成本,而且还不能确保最后重构的不会出现别的问题,尤其是在测试阶段时,这个给产品质量带来严重风险;

内存管理的良好实践

1、模块化设计:保证每个定义的对象都是能被溯源的,避免由于对象被到处引用,在内存释放的时候,造成对象没办法得到释放;

2、事件的绑定、解绑:所有绑定的事件,在模块退出的时候,都需要解绑,如果事件一直都存在,相当于被引用,除了页面关闭,否则内存是没办法被浏览器回收的;

3、ES6中,慎用const对象,由于const对象是无法再进行重新赋值的,在需要释放内存时,会造成内存泄露,当然对于一些常量,不需要进行更改而且需要在很多地方使用,可以使用const

4、使用export的对象,在外部是无法重新赋值的,不建议初始化后的对象export到外部去,使用模块化设计的方式,进行分解抽象,一般都是可以解决问题的;

5、chrome内存监测工具的使用:TimelineProfiles

三、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中,父级节点比较多,比较好的做法是先选取父节点,在选取子节点,可以加快jqueryDOM选取速度;
(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、浏览器的渲染、重绘、重排 扩展学习