记一次工具优化历程

记一次工具优化历程

最近公司搞了一系列的专题活动。因为不是公司的主线任务,所以后台的同学支援的也很有限。提供了一系列的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()

原文地址:https://www.cnblogs.com/marvinemao/p/5875944.html