前端优化之节流和去抖

说到前端优化,如果你不知道节流去抖 那可能真不能说你了解前端优化,但是会节流和去抖,并不能代表你前端优化你很了解,因为前端优化包括方方面面啊。。。

接下来说说节流和去抖

节流

  <input type="text" id="ipt">
  <!-- 开源节流 -->
  <script>
    // 现实中节流 : 减少流通,节省开支 ;
    // 程序中节流 : 减少程序的运行次数; 

     节流:在固定间隔事件之内只能执行一次;
    // 表示是否存在定时器;
    var count = 0;
    // 定义一个变量 用于判断状态
    var t = null;
    document.getElementById("ipt").oninput = function () {
      // console.log(count);
      // 这时候有程序在100毫秒之内执行过,不需要再次执行了;
      if (t !== null) {
        return false;
      }
      t = setTimeout(function () {
        count++;
        console.log(count);
        // 程序500毫秒时间间隔已过,可以继续执行程序了。
        // 我们改变t的状态就可以了。
        t = null;
      }, 500)
    }

    // 什么叫做函数节流 :
    // 1. 是在高频词执行的函数之中 比如:oninput keydown onscroll mousemove;
    // 2. 在函数执行的时候开启一个定时器,记录程序的执行状态;
    // 3. 如果程序执行,判断上一个定时器是否执行完成,如果执行完成那么我们可以重新执行程序,如果没有完成,就跳出程序;

  </script>

使用节流对比如下

在这里插入图片描述
在这里插入图片描述

使用场景(当发送http请求)

当ajax发送http请求时,如果没有加节流,可能使程序在高频率执行函数中一直发送http请求,非常消耗程序性能,而当加入节流后,控制程序执行 相当于间接控制发送http请求的次数。

去抖

 <input type="text" id="ipt">
  <script>
  
    去抖:让函数在停止触发事件后的一段时间再执行
    或者说: 如果函数重复执行,那么就清空计时重新记录
    
    var count = 0;
    var t = null;
    document.getElementById("ipt").oninput = function () {
      // 每次都先清空之前的定时器 再执行下面代码
      clearInterval(t);
      // 当500毫秒之后再执行下面代码
      t = setTimeout(function () {
        console.log("执行");
      }, 500)
    }

    // 函数去抖是一种心态,修行;
    // 注意:去抖会让用户感觉程序会有一丝停顿,所以节流和去抖还是的合理使用
    // 去抖使用:瀑布流

使用去抖对比如下

在这里插入图片描述
在这里插入图片描述

使用场景

瀑布流的去抖使用

提问:为什么这里用去抖而不用节流呢?
解释:因为用户鼠标可能会上下划动,当向上划动时,没必要请求数据,所以这里用去抖会比节流会优化好一些,但是还是根据实际用户体验为准

总结对比

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    button {
      margin: 10px 5px 10px 20px;
    }
  </style>
</head>

<body>

  <button id="btn1">正常加一</button> <label for="add" id="addNum1"></label><br>
  <button id="btn2">节流加一</button> <label for="add" id="addNum2"></label><br>
  <button id="btn3">去抖加一</button> <label for="add" id="addNum3"></label><br>

  <script>

    // 正常点击
    var count1 = 0
    document.querySelector("#btn1").onclick = function () {
      document.querySelector("#addNum1").innerHTML = count1++
    }

    // 节流点击
    var count2 = 0
    var t2 = null
    document.querySelector("#btn2").onclick = function () {
      if (t2 !== null) {
        return false;
      }
      t2 = setTimeout(function () {
        document.querySelector("#addNum2").innerHTML = count2++
        t2 = null
      }, 500)

    }

    // 去抖点击
    var count3 = 0
    var t3 = null
    document.querySelector("#btn3").onclick = function () {
      clearTimeout(t3)
      t3 = setTimeout(function () {
        document.querySelector("#addNum3").innerHTML = count3++
      }, 500)
    }

  </script>

</body>

</html>

在这里插入图片描述

应用节流:某度搜索框提示

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>某度模糊搜索</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    ul,
    li {
      list-style: none;
    }

    #box {
      width: 400px;
      margin: 0 auto;
    }

    #ipt {
      width: 400px;
    }

    .list {
      padding: 0 30px;
      width: 400px;
      border: 1px solid #ccc;
      display: none;
    }

    .list li {
      height: 20px;
      margin-bottom: 5px;
    }
  </style>
</head>

<body>
  <div id="box">
    <input type="text" id="ipt">
    <ul class="list">

    </ul>
  </div>
  <script src="./utils.js"></script>
  <script>
    function $(className) {
      return document.querySelector(className)
    }

    var t = null

    $("#ipt").oninput = function () {
      // 获取输入框的内容
      var ipt_value = ipt.value
      // 如果内容为空,则不用发请求
      if (ipt_value === "") {
        $(".list").style.display = "none"
        return false
      }
      
      // 判断上一次开启定时器后,t的值变化 
      // 500毫秒内 t 不为null 则跳出函数,不执行下面的发送请求
      // 当 500毫秒后 t 为null 则执行下面发送请求 
      // 通过这种形式 来减少 http请求次数 达到 节流 效果
      if (t !== null) {
        return false
      }
      t = setTimeout(function () {
        // ajax 发送 http 请求
        ajax({
          method: "jsonp",
          url: "https://www.baidu.com/sugrec",
          data: {
            pre: 1,
            p: 3,
            ie: "utf-8",
            json: 1,
            prod: "pc",
            from: "pc_web",
            sugsid: "32218,1425,31672,32139,31254,32045,32230,32299,31639",
            // wd: "hello world",
            wd: ipt_value,
            req: 2,
            csor: 5,
            cb: "callback",
            _: Date.now(),
            // 修改jsonpcallback的名字 注意调用ajax时 要把jsonpcallback:"callback"放在data里面
            jsonpcallback: "callback",
          },
          // 回调函数 调用 callbackFun 函数
          callback: callbackFun
        })

        function callbackFun(data) {
          // console.log(data) // 拿到数据可以渲染
          // 注意:当有些内容并没有被搜索 会返回undefined 所以需要判断这里
          console.log(data.g)
          let datas = data.g
          if (datas) {
            // 如果有搜索内容,则显示list 
            $(".list").style.display = "block"
          } else {
            // 如果没有搜索内容出现,则让list为none 并清除里面的内容
            $(".list").style.display = "none"
            $(".list").innerHTML = ""
            // 退出函数,不执行下面的渲染页面内容
            return false
          }
          // 渲染数据到页面
          let html = ""
          for (var i = 0; i < datas.length; i++) {
            html += `<li>${datas[i].q}</li>`
          }
          $(".list").innerHTML = html
        }
        t = null
      }, 500)


      // // 另一种写法 :ajax 和 promise 的封装函数 使用
      // t = setTimeout(function () {
      //   // ajax 和 promise 的封装函数 使用
      //   ajax({
      //     method: "jsonp",
      //     url: "https://www.baidu.com/sugrec",
      //     data: {
      //       pre: 1,
      //       p: 3,
      //       ie: "utf-8",
      //       json: 1,
      //       prod: "pc",
      //       from: "pc_web",
      //       sugsid: "32218,1425,31672,32139,31254,32045,32230,32299,31639",
      //       // wd: "hello world",
      //       wd: ipt_value,
      //       req: 2,
      //       csor: 5,
      //       cb: "callback",
      //       _: Date.now()
      //     }
      //   })
      //     .then(function (res) {
      //       // console.log(res) // 拿到数据可以渲染
      //       // 注意:当有些内容并没有被搜索 会返回undefined 所以需要判断这里
      //       console.log(res.g)
      //       let datas = res.g
      //       if (datas) {
      //         // 如果有搜索内容,则显示list 
      //         $(".list").style.display = "block"
      //       } else {
      //         // 如果没有搜索内容出现,则让list为none 并清除里面的内容
      //         $(".list").style.display = "none"
      //         $(".list").innerHTML = ""
      //         // 退出函数,不执行下面的渲染页面内容
      //         return false
      //       }
      //       // 渲染数据到页面
      //       let html = ""
      //       for (var i = 0; i < datas.length; i++) {
      //         html += `<li>${datas[i].q}</li>`
      //       }
      //       $(".list").innerHTML = html
      //     })
      //   t = null
      // }, 500)
    }

  </script>
</body>

</html>

utils.js封装函数

/**
 * formate : 序列化 GET请求的 URL;
 *   
 * formate( [ url | string ] , data | object );
 *   
 * @return 
 * 
 * 1. key=value;
 * 2. url?key=value;
 * 
 * */

function formate(url, data) {
  var type = "GET";
  if (typeof url === "object" && !(url instanceof Array)) {
    data = url;
    type = "POST";
    url = "";
  }
  var start = true;
  for (var key in data) {
    if (type === "GET") {
      url += (start ? "?" : "&") + key + "=" + data[key];
    } else {
      url += (start ? "" : "&") + key + "=" + data[key];
    }
    start = false;
  }
  return url;
}

/**
 * ajax : 发送ajax请求
 *   
 * ajax( method | string  , url | string  , callback | function , data | object );
 *   
 * @return xhr 
 * 
 * */
function ajax(options) {
  return new Promise(function (resolve, reject) {
    // 参数优化为了啥?
    // 增加默认参数;
    // 对象合并;
    options = Object.assign({
      method: "GET",
      callback: function () {},
      url: "",
      data: {},
      // jsonp形式的回调函数名
      jsonpcallback: "callback"
    }, options);

    if (options.method === "jsonp") {
      // 请求发送;
      var script = document.createElement("script");
      options.data.jsonpcallback = options.data.jsonpcallback ? options.data.jsonpcallback : "callback"
      script.src = formate(options.url, options.data);
      document.body.appendChild(script);
      window[options.data.jsonpcallback] = function (data) {
        options.callback(data);
        resolve(data);
      }
      script.onload = function () {
        script.remove();
      }
    } else {
      var xhr = new XMLHttpRequest();
      xhr.open(options.method, options.method.toUpperCase() === "GET" ? formate(options.url, options.data) : options.url);
      if (options.method.toUpperCase() === "POST") {
        xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
      }
      xhr.send(options.method.toUpperCase() === "POST" ? formate(options.data) : null);
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && /^2\d{2}/.test(xhr.status)) {
          options.callback(xhr.responseText)
          resolve(xhr.responseText)
        }
      }
    }
  })
}

效果图如下:
在这里插入图片描述
在这里插入图片描述
以优化为主,但是还要结合用户体验效果,总的来说,优化是开发中重要的一部分。。。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值