欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

记一次工具优化历程

程序员文章站 2022-04-28 15:43:33
...

记一次工具优化历程

最近公司搞了一系列的专题活动。因为不是公司的主线任务,所以后台的同学支援的也很有限。提供了一系列的ajax接口供前端同学调用,紧赶慢赶也是把工作完满结束。然后回过头来一看,发现很多重复的接口数据调用,并且数据结构也是一样的。后台的同学是把所有的数据字段都发到的前端,所有要用那些字段全凭需要。所以就想能不能进行一次数据接口请求的封装,在下次需要的时候只需要调用然后所有的事情就解决了。这样就可以减轻前端同学重复的垒代码。既然想好了就开始干。

先理理需求

  • 统一的接口调用
  • 数据与文档组装返回
  • 有请求延时需求

其实需求很简单,对应的解决方案也没什么挑战难度的。说说我想的具体思路吧!

  1. 首先对于接口的统一调用,就是以参数形式构造函数的方式来触发。
  2. 拿到后台返回值后与dom组装进行,将数据组装为统一的dom结构(因为所有的数据都返回过来了)。对于不需要的数据用css来进行隐藏。
  3. 对于延时请求,首先我想到的是用setTimeout来进行延时。但最终放弃了,感觉这样不太明智(也没什么技术含量,哈哈~)。只是没有将所有请求延后,所以想是否可以到哪个块儿就请求那个块儿的数据。所以想用图片懒加载的思路来解决延时。
    说了这么多还是直接上代码吧。
var utils = (function(mod) {

  /**
   * [ajaxAPI]
   * ajax 获取问文章接口
   * @author mao
   * @version 1
   * @date    2016-09-08
   * @param   {Object}   option {url:请求地址,count:显示条数,elem:dom元素,flag:延时请求true || false,type:class类类型}
   */
  mod.ajaxAPI = function(option) {
    var url  = option.url,
        count = option.count || 3,
        elem = option.elem,
        flag = option.flag || false,
        type = option.type || '';


    count = parseInt(count);

    //判断是否延时
    if(!flag) {
      getData(url, count, elem, type);
    } else {
      //轮动检测
      $(window).scroll(function() {
        //dom块儿是否进入视口
        var Height = $(elem).offset().top - ($(document).scrollTop() + $(elem).height() + $(window).height());
        if(Height < 0) {
          //获取判断参数。为1则不执行,否则执行
          var isDone = $(elem).attr('data-load');
          if(!isDone) {
            //ajax请求
            getData(url, count, elem, type);
            //设置判断参数
            $(elem).attr('data-load', 1);
          }
        }
      });
    }
  }

  /**
   * ajax请求数据
   * @author 1
   * @version version
   * @date    2016-09-06
   * @param   {[string]}   url   [请求地址]
   * @param   {[number]}   count [显示条数]
   * @param   {[string]}   elem  [dom元素]
   * @return  {[type]}         [description]
   */
  function getData(url, count, elem, type) {
    $.ajax({
      type:'get',
      url:url,
      dataType:'json',
      success:function(msg) {
        var str = '';
        //判断count是否超过需要的条数
        count = count > msg.length ? msg.length:count; 
        for(var i = 0; i < count; i++) {
          //将数据加载到不同结构的dom中
          str += domType(msg[i], type);
        }
        $(elem).append(str);
      }
    });
  }

  /**
   * dom类型结构
   * @author mao
   * @version 1
   * @date    2016-09-07
   * @param   {object}   data 传入数据
   * @param   {number}   type 选择的dom结构
   */
  function domType(data, type) {

    var str = '<li class="'+type+'">'+
                '<a href="'+data.article_url+'" target="_blank">'+
                  '<img src="'+data.thumbnail_url+'">'+
                  '<div>'+
                    '<p>'+data.title+'</p>'+
                    '<span>'+data.digest+'</span>'+
                    '<abbr>'+data.published_at+'</abbr>'+
                    '</div>'+
                '</a>'+
              '</li>';
    return str;
  }

  return mod;

})(utils || {});

这段代码出来之后基本是解决了提出的三个需求,满足实际的要求。对于前端同学的调用也是非常方便的。在跟同事分享之后,同事提了一个问题就是在绑定滚轮事件哪里。在每次调用的都会去绑定事件,相当于会在每一个调用的地方都会绑定一个回调。对于事件绑定的回调消耗会很大,这里可以改进一下。每次调用不去绑定,而是在所有的初始化参数函数准备好后再给予一次绑定,这样就不会有多次绑定事件回调的消耗问题。

为了解决这个问题,我一开始的思路是将事件绑定在以一个接口开放出来。在所有的函数初始化后再统一调用这个事件绑定。这样就需要将所有的参数暂时保存下来,在需要的时候再去拿。所以我想将他们保存在utils的内部,每次初始化参数一进来就保存。
修改的绑定部分如下:

mod.deferScroll = function() {
    $(window).scroll(function() {
        //对每一个dom元素进行绑定
        $.each(obj, function(key, value) {
            //dom块儿是否进入视口
        var Height = $(value.elem).offset().top - ($(document).scrollTop() + $(value.elem).height() + $(window).height());
        if(Height < 0) {
          //获取判断参数。为1则不执行,否则执行
          var isDone = $(value.elem).attr('data-load');
          if(!isDone) {
            //ajax请求
            //console.log('视口延时', value);
            getData(value);
            //设置判断参数
            $(value.elem).attr('data-load', 1);
          }
        }
        });
    });
}

上面代码中的obj就是保存下来的参数数组。
这样问题又来了,这样就会再向外部开放一个接口。都出来一组调用,对于调用使用就不是很方便了。所以想是否可以将这个函数放在内部,在调用的时候进入一次。所以想到可以这样

    mod.dataDom = function(option) {
        option.url  = option.url,
        option.count = option.count || 3,
        option.elem = option.elem,
        option.flag = option.flag || false,
        option.type = option.type || '';

        //存储数据
        if(option.flag) {
            obj.push(option);
        } else {
            //数据请求
            getData(option);
            //console.log('数据请求', option);
        }

        //让所有元素准备好,执行一次
        if(!static_defer) {
          //时间延时
          deferTime();
          //视口延时
          deferScroll();
          static_defer = true;
        }
    }

上面代码中多加了个内部变量static_defer,初始值设置为false。这样是指进行一次绑定,但是只有第一个初始化函数能够进入,后面的参数就没有绑定上。怎样才能让所有的参数准备好后才进行绑定。幸运的是javascript的事件循环,能够解决这个问题。将上述代码中加入setTimeout这个定时函数,会将setTimeout里的回调函数放入任务队列中,在主线程执行完之后再执行。这就使能所有的参数保存下来了再进行事件绑定。
修改如下:

mod.dataDom = function(option) {
        option.url  = option.url,
        option.count = option.count || 3,
        option.elem = option.elem,
        option.flag = option.flag || false,
        option.type = option.type || '';

        //存储数据
        if(option.flag) {
            obj.push(option);
        } else {
            //数据请求
            getData(option);
            //console.log('数据请求', option);
        }

        //让所有元素准备好,执行一次
        if(!static_defer) {
          setTimeout(function() {
            //时间延时
            deferTime();
            //视口延时
            deferScroll();
          }, 0);
          static_defer = true;
        }
    }

这样不仅满足了需求,也对事件绑定进行了一次优化。

参考文档

JavaScript 运行机制详解:再谈Event Loop
The Node.js Event Loop, Timers, and process.nextTick()
Node.js Event Loop 的理解 Timers,process.nextTick()