防流、节抖、重绘、回流原理,以及实现方法和区别,还有就是为什么会出现这种情况?
防抖(Debounce)
原理
防抖就像是你坐电梯,如果你一直不停地按开门按钮,电梯不会每次都开门,而是等你停止按按钮一段时间后,才会执行开门动作。在前端里,当一个事件被频繁触发时,防抖会让事件处理函数在你停止触发一段时间后才执行。比如在搜索框输入联想功能中,用户快速输入文字时,我们不希望每次输入都去请求服务器获取联想词,而是等用户停止输入一小段时间后,再去请求,这样可以减少不必要的请求。
实现方法
javascript
function debounce(func, delay) {
let timer;
return function () {
const context = this;
const args = arguments;
// 每次触发事件时,清除之前的定时器
clearTimeout(timer);
// 设置新的定时器
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
};
}
// 使用示例
const input = document.getElementById('searchInput');
input.addEventListener('input', debounce(function () {
console.log('执行搜索操作');
}, 300));
出现原因
在一些场景中,用户可能会频繁触发某个事件,比如搜索框输入、窗口缩放等。如果每次触发都执行相应的处理函数,会导致性能问题,比如产生大量的网络请求、进行不必要的计算等。防抖可以避免这些问题,提高性能和用户体验。
节流(Throttle)
原理
节流就像你家的水龙头,你把水流调到一定大小,不管你怎么拧水龙头,在一段时间内,水的流量是固定的。在前端里,当一个事件被频繁触发时,节流会让事件处理函数在一定时间内只执行一次。比如在滚动加载数据的场景中,用户滚动页面时,我们不希望滚动一点就加载一次数据,而是每隔一段时间加载一次。
实现方法
javascript
function throttle(func, limit) {
let inThrottle;
return function () {
const context = this;
const args = arguments;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
// 设置定时器,一段时间后重置状态
setTimeout(() => inThrottle = false, limit);
}
};
}
// 使用示例
window.addEventListener('scroll', throttle(function () {
console.log('滚动加载数据');
}, 200));
出现原因
和防抖类似,在高频触发事件的场景中,为了避免处理函数被过于频繁地执行,导致性能问题,比如滚动加载数据时,如果每次滚动都加载数据,会造成大量的网络请求和数据处理,影响性能。节流可以控制处理函数的执行频率,保证性能稳定。
重绘(Repaint)
原理
重绘就像是你给家里的家具重新刷漆,家具的位置和大小不变,只是外观变了。在网页里,当一个元素的外观发生变化,但没有影响到布局信息(如宽高、位置)时,浏览器会将这个元素的外观重新绘制一遍,这就是重绘。比如改变元素的颜色、透明度等样式。
实现方法
在 CSS 里直接修改元素的外观样式就会触发重绘,例如:
css
/* HTML 中有一个 id 为 myElement 的元素 */
#myElement {
color: red; /* 初始颜色为红色 */
}
/* 使用 JavaScript 修改颜色,触发重绘 */
const element = document.getElementById('myElement');
element.style.color = 'blue';
出现原因
当我们通过 CSS 或 JavaScript 修改元素的外观样式时,浏览器需要更新元素在屏幕上的显示,所以会触发重绘。
回流(Reflow,也叫重排)
原理
回流就像是你重新布置家里的家具,改变了家具的位置和大小。在网页里,当一个元素的布局信息(如宽高、位置、边距等)发生变化时,浏览器需要重新计算元素的位置和大小,然后将它们重新排列在页面上,这就是回流。回流的开销比重绘大,因为它不仅要重新绘制元素,还要重新计算布局。
实现方法
修改元素的布局信息会触发回流,例如:
javascript
const element = document.getElementById('myElement');
// 修改元素的宽度,触发回流
element.style.width = '200px';
出现原因
当我们通过 CSS 或 JavaScript 修改元素的布局信息时,浏览器需要重新计算整个页面的布局,所以会触发回流。像添加或删除元素、修改元素的尺寸、改变字体大小等操作都可能导致回流。
区别
- 防抖和节流:防抖是在用户停止触发事件一段时间后才执行处理函数,强调的是"停止触发后的延迟执行";节流是在一定时间内只执行一次处理函数,强调的是"固定时间间隔执行"。
- 重绘和回流:重绘只涉及元素外观的重新绘制,不改变布局信息;回流涉及元素布局信息的重新计算和排列,开销比重绘大。而且回流通常会触发重绘,因为布局改变后,元素的外观也需要重新绘制。
那么,在vue和react中如何进行防流、节抖、重绘、回流原理,以及实现方法和区别
防抖(Debounce)
原理
想象你在电梯里,要是一直狂按开门按钮,电梯不会每次都开门,而是等你停手一小段时间后,才会执行开门动作。在前端里,防抖就是处理频繁触发事件的这种情况,比如搜索框输入,用户快速打字时,我们不想每次按键都去做一些操作(像请求服务器获取联想词),而是等用户停了一小会儿,再去执行对应的操作。
在 Vue 中的实现方法
在 Vue 里,我们可以借助第三方库 lodash
来实现防抖。比如在一个搜索框输入时使用防抖:
vue
<template>
<div>
<input v-model="keyword" @input="debouncedSearch" placeholder="搜索内容">
</div>
</template>
<script>
import { debounce } from 'lodash';
export default {
data() {
return {
keyword: ''
};
},
created() {
// 创建防抖函数,300 毫秒内没有再次触发才执行搜索
this.debouncedSearch = debounce(this.search, 300);
},
methods: {
search() {
console.log('执行搜索:', this.keyword);
// 这里可以放实际的搜索请求代码
}
}
};
</script>
这里在 created
生命周期里用 lodash
的 debounce
函数把 search
方法包装成了防抖函数 debouncedSearch
,这样输入框输入事件触发时,只有停顿 300 毫秒才会执行搜索操作。
在 React 中的实现方法
在 React 里同样可以用 lodash
来做防抖。示例如下:
jsx
import React, { useState } from'react';
import { debounce } from 'lodash';
function SearchInput() {
const [keyword, setKeyword] = useState('');
const search = () => {
console.log('执行搜索:', keyword);
// 这里可以放实际的搜索请求代码
};
const debouncedSearch = debounce(search, 300);
const handleInputChange = (e) => {
setKeyword(e.target.value);
debouncedSearch();
};
return (
<input
type="text"
value={keyword}
onChange={handleInputChange}
placeholder="搜索内容"
/>
);
}
export default SearchInput;
这里创建了一个 debouncedSearch
防抖函数,当输入框内容改变时,调用这个防抖函数,从而实现防抖效果。
出现原因
在很多场景下,用户可能会快速频繁地触发事件,比如输入框输入、窗口缩放等。如果每次触发都执行对应的操作,会造成性能问题,像产生大量不必要的网络请求或者进行重复计算,所以需要防抖来避免这些问题。
节流(Throttle)
原理
节流就像家里的水龙头,你把水流大小固定好,不管你怎么拧水龙头,在一段时间内,水的流量是固定的。在前端里,当一个事件被频繁触发时,节流会让事件处理函数在一定时间内只执行一次。比如滚动加载数据,我们不想滚动一点就加载一次数据,而是每隔一段时间加载一次。
在 Vue 中的实现方法
vue
<template>
<div @scroll="throttledScroll">
<!-- 这里放滚动内容 -->
</div>
</template>
<script>
import { throttle } from 'lodash';
export default {
created() {
// 创建节流函数,每 200 毫秒最多执行一次滚动处理
this.throttledScroll = throttle(this.handleScroll, 200);
},
methods: {
handleScroll() {
console.log('滚动事件触发');
// 这里可以放滚动加载数据的代码
}
}
};
</script>
使用 lodash
的 throttle
函数把 handleScroll
方法包装成节流函数 throttledScroll
,这样滚动事件触发时,每 200 毫秒最多执行一次 handleScroll
。
在 React 中的实现方法
jsx
import React from'react';
import { throttle } from 'lodash';
function ScrollComponent() {
const handleScroll = () => {
console.log('滚动事件触发');
// 这里可以放滚动加载数据的代码
};
const throttledScroll = throttle(handleScroll, 200);
return (
<div onScroll={throttledScroll}>
{/* 这里放滚动内容 */}
</div>
);
}
export default ScrollComponent;
同样借助 lodash
的 throttle
函数创建节流函数,绑定到滚动事件上。
出现原因
和防抖类似,在高频触发事件的场景中,为了避免处理函数被过于频繁地执行,导致性能问题,比如滚动加载数据时,如果每次滚动都加载数据,会造成大量的网络请求和数据处理,影响性能,所以需要节流来控制执行频率。
重绘(Repaint)
原理
重绘就好比你给家里的家具重新刷漆,家具的位置和大小不变,只是外观变了。在网页里,当一个元素的外观发生变化,但没有影响到布局信息(像宽高、位置)时,浏览器会把这个元素的外观重新画一遍,这就是重绘。比如改变元素的颜色、透明度等样式。
在 Vue 和 React 中出现的情况及处理
在 Vue 和 React 里,只要修改了元素的外观样式,就可能触发重绘。比如在 Vue 中:
vue
<template>
<div :style="{ color: textColor }">这是一段文字</div>
<button @click="changeColor">改变颜色</button>
</template>
<script>
export default {
data() {
return {
textColor: 'black'
};
},
methods: {
changeColor() {
this.textColor = 'red';
}
}
};
</script>
点击按钮改变文字颜色,就会触发重绘。在 React 里也是类似的,通过修改样式来触发重绘:
jsx
import React, { useState } from'react';
function RepaintExample() {
const [textColor, setTextColor] = useState('black');
const changeColor = () => {
setTextColor('red');
};
return (
<div>
<div style={{ color: textColor }}>这是一段文字</div>
<button onClick={changeColor}>改变颜色</button>
</div>
);
}
export default RepaintExample;
出现原因
当我们通过 CSS 或 JavaScript 修改元素的外观样式时,浏览器需要更新元素在屏幕上的显示,所以会触发重绘。
回流(Reflow,也叫重排)
原理
回流就像是你重新布置家里的家具,改变了家具的位置和大小。在网页里,当一个元素的布局信息(像宽高、位置、边距等)发生变化时,浏览器需要重新计算元素的位置和大小,然后把它们重新排列在页面上,这就是回流。回流的开销比重绘大,因为它不仅要重新画元素,还要重新算布局。
在 Vue 和 React 中出现的情况及处理
在 Vue 里,比如动态修改元素的宽度就可能触发回流:
vue
<template>
<div :style="{ width: boxWidth + 'px' }">这是一个盒子</div>
<button @click="changeWidth">改变宽度</button>
</template>
<script>
export default {
data() {
return {
boxWidth: 100
};
},
methods: {
changeWidth() {
this.boxWidth = 200;
}
}
};
</script>
在 React 中:
jsx
import React, { useState } from'react';
function ReflowExample() {
const [boxWidth, setBoxWidth] = useState(100);
const changeWidth = () => {
setBoxWidth(200);
};
return (
<div>
<div style={{ width: `${boxWidth}px` }}>这是一个盒子</div>
<button onClick={changeWidth}>改变宽度</button>
</div>
);
}
export default ReflowExample;
点击按钮改变盒子宽度,就会触发回流。
出现原因
当我们通过 CSS 或 JavaScript 修改元素的布局信息时,浏览器需要重新计算整个页面的布局,所以会触发回流。像添加或删除元素、修改元素的尺寸、改变字体大小等操作都可能导致回流。
区别
- 防抖和节流:防抖是等用户停止触发事件一段时间后才执行处理函数,强调"停止触发后的延迟执行";节流是在一定时间内只执行一次处理函数,强调"固定时间间隔执行"。
- 重绘和回流:重绘只改元素外观,不涉及布局信息改变;回流涉及元素布局信息重新计算和排列,开销比重绘大,而且回流通常会引发重绘。