• 工作笔记——js前端规范


      去年年末做了一个项目,因为第一次做前端管理职位,第一次做整个项目的前端架构很多东西都不熟悉,作为一次大胆的尝试。

      js方面的只有一个坑,那就是前端与后端的网络层封装,这一块是在后端的协助下开发的。网络层封装的过程中学到一点就是参数写成对象的形式是最佳实践,类似的还有消息提示框组件同理。

      项目开发过程中遇到一个比较麻烦的问题,由于项目周期比较短,为了促进开发进度,我提议让UI和前端同时进行,其实现在想一下,这是一个错误的决定,因为这导致后期项目修改bug的时候有相当一段时间内都是在修改UI层面的东西,需求一直修改也导致项目后期不停地修改ui。最主要是项目周期太短,团队协作没有经验,导致后期花费太多时间在一些细节上,而这些细节应该在项目初期解决,不是在项目完成之后里面改来改去。

      所以这次项目结束之后就和UI探讨项目开发的规范设计。后期讨论的结果是最好以组件的形式来设计ui  

      a:模态对话框的ui设计稿

      b:选项卡的ui设计稿

         c:表格的ui设计稿

         d:常用组件的ui设计稿

         e:文字层面设计到正文字体,正文大小,正文文字颜色;一级标题字体大小,字体颜色;二级标题字体大小,字体颜色;

         f:表单ui设计,表单中还设计到左侧的描述文字的字体大小,文字颜色,与右侧input的距离,以及input的边框颜色,边框圆角是多少。

         h:font-family: "Microsoft YaHei","微软雅黑",Tahoma,Arial,Helvetica,STHeiti;其中,后面两张字体是苹果默认的字体,最佳实践应该是:font-family: "Microsoft YaHei","微软雅黑";因为不同的字体会产生不同的行间距和布局。

      

    属性 属性值
    字体 整站字体、正文、标题、字体颜色、字体大小、高亮字体(正常时的颜色,hover时的颜色)
    modal title、body、footer、timer(定时器)
    tab 活动的时候的样式、正常的时候的样式
    table 表格的高度、对齐方式、hover时候的行颜色、无内容时的UI
    无内容 无内容时的UI
    分页组件 上一页、下一页、翻页
    根据业务提取的组件 业务逻辑一定要清晰

     编写js组件时,需要编写输入和输出都比较灵活的函数,比如模态对话框有可能需要定时器,有可能只需要一个按钮,有可能需要多个按钮,这些情况都需要考虑到。

    以下是需要注意的几点

    1、网络层封装,网络层应该封装在一个单例里面,单例名称定位HttpUtils;

    e.g

      1 /**
      2 * @Author Mona
      3 * @Date 2016-12-08
      4 * @description 网络层封装
      5 */
      6 
      7 /**
      8  * 封装基本请求方式
      9  */
     10 window.BaseRequest = (function () {
     11     //发送请求的所有方式
     12     var request = {};
     13 
     14     /**
     15      * 基本请求
     16      */
     17     function baseRequestFunc(type,param,url,async,contentType,dataType,processData,opt_suc,paramType,opt_error) {
     18         var cur_url = contextPath+url;
     19         var now_url = '';
     20         //当把参数作为路由的一部分时此时的参数为字符串;
     21         (paramType&&paramType=='url')?(now_url=cur_url+'/'+param+'.json'):(now_url=cur_url+'.json');
     22         //这个里面是最基本的ajax
     23         $.ajax({
     24             type:type,
     25             data:param,
     26             url:now_url,
     27             async:async,//默认为true
     28             contentType:contentType,//默认为application/x-www-form-urlencoded
     29             dataType:dataType,//默认为预期服务器返回的数据类型
     30             processData:processData,//默认为true
     31             success:function(data,textStatus,jqXHR){
     32                 if($.isFunction(opt_suc)){
     33                     opt_suc(data,textStatus,jqXHR);
     34                 }
     35             },
     36             error:function(jqXHR,textStatus,errorThrown){
                //其实,这里应该处理的更加灵活,比如说可以配置为可以选择使用默认的后端错误提示,或者是使用自己写的错误提示方式
    37 renderErrorMsg(jqXHR,textStatus,errorThrown); 38 if($.isFunction(opt_error)){ 39 opt_error(); 40 } 41 } 42 }) 43 44 } 45 46 /** 47 * get异步请求方式 48 */ 49 request.get = function (param,url,callback,paramType) { 50 baseRequestFunc('get',param,url,true,'application/x-www-form-urlencoded','json',true,callback,paramType); 51 } 52 53 /** 54 * get同步请求方式 55 * param {param} object 56 */ 57 request.sync_get = function (param,url,callback,paramType) { 58 baseRequestFunc('get',param,url,false,'application/x-www-form-urlencoded','json',true,callback,paramType); 59 } 60 61 /** 62 * post异步请求方式 63 * param {param} object 64 */ 65 request.post = function (param,url,callback,paramType) { 66 baseRequestFunc('post',param,url,true,'application/json','json',true,callback,paramType); 67 } 68 69 /** 70 * post的requestBean请求方式 这种请求方式适用于字段较多,且需要formdata方式上传文件 71 * param {param} object {param.files} array {param.fileNames} array {param.inputData} object 72 */ 73 request.post_multipart_form_data = function (param,url,callback,paramType,formData,opt_error) { 74 var form_data = new FormData(); 75 if(param.files && param.files.length>0){ 76 $.each(param.files,function(k,info_name){ 77 //if(document.getElementById(info_name).files[0] !== undefined){ 78 form_data.append('files',document.getElementById(info_name).files[0]) 79 //} 80 }) 81 } 82 83 if(param.fileNames && param.fileNames.length>0){ 84 $.each(param.fileNames,function(i,item){ 85 form_data.append('fileNames',item); 86 }) 87 } 88 if(formData && formData=='formdata'){ 89 $.each(param.inputData,function(i,item){ 90 form_data.append(i,item); 91 }) 92 }else{ 93 form_data.append('requestBean', new Blob([JSON.stringify(param.inputData)], { 94 type: "application/json" 95 })); 96 } 97 baseRequestFunc('post',form_data,url,true,false,'json',false,callback,paramType,opt_error); 98 } 99 100 /** 101 * post的formdata请求方式 102 * param {param} object 103 */ 104 request.post_form_data = function(param,url,callback,paramType,opt_error){ 105 var form_data = new FormData(); 106 $.each(param,function(i,item){ 107 form_data.append(i,item); 108 }) 109 baseRequestFunc('post',form_data,url,true,false,'json',false,callback,opt_error); 110 } 111 112 /** 113 * post的JSON.stringify(param)请求方式 114 * param {param} object 115 */ 116 request.post_string_data = function(param,url,callback,paramType,opt_error){ 117 var cur_data = JSON.stringify(param); 118 baseRequestFunc('post',cur_data,url,true,'application/json','json',false,callback,paramType,opt_error); 119 } 120 return request; 121 })();
     1 var HttpUtils = (function () {
     2 
     3 //融资申请流程所有请求接口对象
     4 var application = {};
     5 
    6 /**
    7 * 获取融资申请回显信息
    8 */
    9 application.get_finance_info_echo_data = function(param,callback){
    10 var url = '/finance/finance_info';
    11 BaseRequest.get(param,url,callback);
    12 }());

    2、数据列表渲染等基础的常用的组件封装;

    e.g:

     1 /**
     2  * @Author Mona
     3  * @date 2016-11-04
     4  * @description 审核结束提交组件
     5  * @param selector {string} 组件最大的容器名称
     6  * @param maxLength {int} 组件中文字输入的最大长度
     7  */
     8 
     9 function controlBtn(type,selector,curValLen){
    10     var _this = this;
    11     _this.selector = selector;
    12     _this.maxlength = 140;
    13     _this.curValLen = curValLen;
    14     _this.type = type;
    15 
    16     _this.select_dom = $(_this.selector).find('select[data-role="control-btn"]');
    17     _this.textarea_dom = $(_this.selector).find('textarea[data-role="control-btn"]');
    18     _this.btn_dom = $(_this.selector).find('[data-role="target-btn"]');
    19     _this.b_dom = $(_this.selector).find('[data-role="font-length"]>b');
    20 
    21     _this.selectVal = $.trim(_this.select_dom.val());
    22     _this.textareaVal = $.trim(_this.textarea_dom.val());
    23 
    24     _this.setOn = function(){
    25         _this.btn_dom.removeAttr('disabled');
    26     }
    27 
    28     _this.setOff = function(){
    29         _this.btn_dom.attr('disabled','');
    30     }
    31 
    32     if(_this.selectVal!==''){
    33         (_this.selectVal=='agree'||_this.type=='1' || _this.textareaVal.length>0)?_this.setOn():_this.setOff()
    34     } else{
    35         _this.setOff()
    36     } 
    37     _this.init();
    38 
    39     curValLen?_this.b_dom.text(_this.maxlength-curValLen):_this.b_dom.text(_this.maxlength);
    40 }
    41 
    42 
    43 controlBtn.prototype = {    
    44     init:function(){
    45         var _this = this;
    46         var curSelectVal = _this.selectVal || '';
    47         var curTextareaVal = _this.textareaVal || '';
    48         
    49         _this.b_dom.text(_this.maxlength-_this.curValLen); 
    50 
    51         _this.select_dom.on('change',function(){
    52             curSelectVal = $.trim($(this).val());
    53             console.debug('意见内容==='+curTextareaVal)
    54             if(curSelectVal!==''){
    55                 (curSelectVal=='agree'||_this.type=='1' || curTextareaVal.length>0)?_this.setOn():_this.setOff()
    56             } else{
    57                 _this.setOff()
    58             }
    59         })
    60 
    61         _this.textarea_dom.bind('input propertychange',function(){
    62             curTextareaVal = $.trim($(this).val()); 
    63             
    64             if(curSelectVal!==''){
    65                 (curSelectVal=='agree'||_this.type=='1' || curTextareaVal.length>0)?_this.setOn():_this.setOff()
    66             } else{
    67                 _this.setOff()
    68             }
    69             if(curTextareaVal.length>0){
    70                 if(curTextareaVal.length>_this.maxlength){
    71                     _this.b_dom.text(0);
    72                     $(this).val($(this).val().substring(0,140));
    73                 }else{
    74                     _this.b_dom.text(_this.maxlength-$(this).val().length);
    75                 }
    76             }else{
    77                 _this.b_dom.text('140');
    78             }
    79 
    80         })
    81 
    82     }
    83 }
     1 /**
     2 * @param {option} object
     3 * option.type tip| operableModal 
     4 * option.title 提示的标题 string 
     5 * option.info 提示的内容 string/dom
     6 * option.historyRef 从哪里来回哪里去 string 
     7 * option.timer 停留的时间 int 
     8 * option.cancelText 左侧取消按钮的文案
     9 * option.successText 右侧成功按钮的文案
    10 * option.cancelCallback 取消的回调函数
    11 * option.successCallback 成功的回调函数
    12 */
    13 function whaleModal(option){
    14     if($('#whale-modal-form').length>0){
    15         $('#whale-modal-form').detach();
    16     }
    17 
    18     if(option && typeof option !=='object'){//如果存在
    19         alert('请传入正确的参数!');
    20     }
    21 
    22     var type = (option&&option.type) || 'tip';
    23     var title = (option&&option.title) || '提示';
    24     var info = (option&&option.info) || '办理成功!';
    25     var removeStore = (option&&option.is_remove_store)||false;
    26 
    27     var historyRef = '';
    28     if(option&&option.historyRef){
    29        historyRef = contextPath+option.historyRef;
    30     }else if(window.sessionStorage["historyRef"]){
    31        historyRef = window.sessionStorage["historyRef"]
    32     }else{
    33        historyRef = contextPath+'/anagement_page';
    34     }
    35 
    36     var timer = (option&&option.timer) || 2000;    
    37     var cancelText = (option&&option.cancelText) || '取消';
    38     var successText = (option&&option.successText) || '成功';
    39     var cancelCallback = option&&option.cancelCallback;
    40     var successCallback = option&&option.successCallback;
    41     var is_operable_modal = (type == 'operableModal');
    42     var cancel_able = (option&&option.cancelAble) || false;
    43 
    44 
    45     var h = '';
    46     h+='<div id="whale-modal-form" class="modal fade" tabindex="-1" style="display: none;" aria-hidden="true" data-role="whale-modal">';
    47    // h+='<div class="modal-backdrop"></div>';
    48     h+='<div class="modal-dialog">';
    49     h+='<div class="modal-content">';
    50     h+='<div class="modal-header">';
    51     h+='<span  class="close" data-dismiss="modal">×</span>';
    52     h+='<h4 class="blue bigger">'+title+'</h4>';
    53     h+='</div>';
    54     h+='<div class="modal-body">'+info+'</div>';
    55     if(is_operable_modal){
    56         h+='<div class="modal-footer">';
    57         cancel_able?(h+='<button class="btn btn-sm btn-default" data-dismiss="modal" data-role="cancel">'+cancelText+'</button>'):'';
    58         h+='<button class="btn btn-sm btn-primary" data-role="success">'+successText+'</button>';
    59         h+='</div>';
    60     }
    61     h+='</div>';
    62     h+='</div>';
    63     h+='</div>';
    64     $('body').append(h);
    65     var cur_modal = $('#whale-modal-form');
    66     var cancel_btn_dom = cur_modal.find('[data-role="cancel"]');
    67     var success_btn_dom = cur_modal.find('[data-role="success"]');
    68     cur_modal.modal('show');
    69 
    70     //以下是给取消确定按钮注册点击事件
    71     if(is_operable_modal){//如果不是需要自己去操作的modal 那么是不需要注册按钮的交互事件的
    72         cancel_btn_dom.on('click',function(){
    73             if($.isFunction(cancelCallback)){
    74                 cancelCallback();
    75             }
    76         });
    77         success_btn_dom.on('click',function(){
    78             if($.isFunction(successCallback)){
    79                 successCallback();
    80             }
    81         });
    82 
    83         $('[id="whale-modal-form"] [data-dismiss="modal"]').on('click',function(){
    84             window.location.href = historyRef;
    85         }) 
    86     }
    87     
    88 
    89     //以下是设置自动跳转的逻辑
    90     if(!is_operable_modal){//如果是需要自己去操作的modal那么就不用自动跳转
    91         setTimeout(function(){
    92             cur_modal.modal('hide');
    93             removeStore?window.sessionStorage.clear():'';
    94             window.location.href = historyRef;
    95         },timer); 
    96     }
    97     
    98 }

    3、【js命名】为了更好的维护后期代码,变量命名以下面的规则命名

    代表dom对象的js变量命名为 xx_dom;

    后端返回的参数为data;

    前端发送给后端的参数为param;

    构造函数的命名首字母大写后面驼峰形式;

    拿到回显数据方法名为 get_echo_data

    设置XX以'set'开头命名方法

    得到XX以'get'开头命名方法

    sessionStorage,localStorage中存储的数据都以驼峰形式设置key

    4、【css命名】ui层面的东西和数据交互层面的东西分开,方便维护

    在dom中不要经常使用id属性以免系统会产生重复,如果一定要使用id那么请加上前缀,比如container-head ,container-body , container-footer

    css样式表中不使用id作为选择器书写样式,一律使用class

    有数据交互功能的dom结构都是用data-role-XX的形式增加属性,方便在js中使用,且css中不使用data-role开头的属性书写样式。

    5、【函数】

    前端开发过程中函数经常用到,这个时候函数的命名以及函数的通用性非常重要

    多人合作开发时函数命名尤其重要【涉及全局(通用)函数和各自使用的函数】普通函数的命名以驼峰形式书写

    通用函数以g开头,比如gModal,gGetRoleInfo

    构造函数首字母大写,后面使用驼峰形式书写

    与网络层相关的方法命名以下划线的方式书写比如 HttpUtils.get_finance_echo_data

    功能 函数名称
    拿到后端返回的回显数据 get_finance_echo_data
    拿到机构信息 get_institution_info_data
       

    复用性比较高的函数使用对象的形式来传入参数,这样方便后期维护,如果参数不是以对象的形式出现,那么后期方法的改造会出现非常大的问题,比如说,网络层封装的函数又可能有其他的业务需求,那么再传参数,就有可能影响其他地方的调用,这个时候使用对象的形式传入参数,会给开发带来极大的便利。

          

  • 相关阅读:
    spring-boot4
    spring-boot3代码
    spring-boot3
    spring-boot2代码
    spring-boot2
    Java Socket编程
    eclipse项目中.classpath文件详解
    spring-boot1
    ASCII 在线转换器
    如何在Android开发中让你的代码更有效率
  • 原文地址:https://www.cnblogs.com/MonaSong/p/6394269.html
Copyright © 2020-2023  润新知