说到前端优化,如果你不知道节流
和去抖
那可能真不能说你了解前端优化,但是会节流和去抖,并不能代表你前端优化你很了解,因为前端优化包括方方面面啊。。。
接下来说说节流和去抖
节流
<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)
}
}
}
})
}
效果图如下:
以优化为主,但是还要结合用户体验效果,总的来说,优化是开发中重要的一部分。。。