什么是防抖和節流?
函數節流(throttle)與 函數防抖(debounce)都是為了限制函數的執行頻次,以優化函數觸發頻率過高導致的響應速度跟不上觸發頻率,出現延遲,假死或卡頓的現象
是應對頻繁觸發事件的優化方案。
防抖(debounce)
防抖就是防止抖動,避免事件的重復觸發。
防抖可以概括為觸發高頻事件后n秒內函數只會執行一次,如果n秒內高頻事件再次被觸發,則重新計算時間。
n 秒后執行該事件,若在n秒后被重復觸發,則重新計時
簡單的說,如果某一事件被連續快速地觸發多次,只會執行最后那一次。
- 使用場景
· input 搜索錄入,用戶不斷錄入值
· window觸發resize事件
· mousemove 鼠標滑動事件
· scroll滾動條滑動請求、上拉觸底加載更多
· 表單驗證,按鈕的提交事件
節流(throttle)
節流就是減少流量,將頻繁觸發的事件減少,并每隔一段時間執行。會控制事件觸發的頻率。所以節流會稀釋函數的執行頻率。
n 秒內只運行一次,若在n秒內重復觸發,只有一次生效。
如果連續快速地觸發多次,在規定的時間內,只觸發一次。如限制1s,則1s內只執行一次,無論怎樣,都在會1s內執行相應的操作。
- 使用場景
· 獲取驗證碼
·鼠標不斷點擊觸發,mousedown(規定時間內只觸發一次)
·mousemove 鼠標滑動事件
·滾動條滑動請求、上拉觸底加載更多
·搜索、提交等按鈕功能
防抖和節流之間的差別:
相同點: 目的都是,降低回調函數的執行頻率,節省計算資源
不同點:
· 防抖,是在一段連續操作結束之后,處理回調
· 節流,是在一段連續操作中,每一段時間只執行一次,在頻率較高的事件中使用來提高性能。
·防抖用于無法預知的用戶主動行為,如用戶輸入內容去服務端動態搜索結果。用戶打字的速度等是無法預知的,具有非規律性。
·節流可用于一些非用戶主動行為或者可預知的用戶主動行為,如用戶滑動商品櫥窗時發送埋點請求、滑動固定的高度是已知的邏輯,具有規律性。
·防抖是關注于最后一次的事件觸發,而節流則是在規定的時間里只執行一次。
·防抖是將多次執行變為最后一次執行,節流是將多次執行變成每隔一段時間執行
- 防抖以一個實時搜索框為例 當用戶在輸入框中進行實時搜索時,頻繁調用接口可能會導致性能問題和不必要的網絡請求。為了優化這種情況,通常可以使用防抖動策略來減少接口調用的頻率。
使用 flutter_hooks 包可以更容易地實現防抖動功能。
flutter_hooks: ^0.18.0
dio: ^5.0.2
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:dio/dio.dart';void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text('Debounced Search'),),body: SearchScreen(),),);}
}class SearchScreen extends HookWidget {@overrideWidget build(BuildContext context) {final searchController = useTextEditingController();final debouncedSearch = useMemoized(() {return _DebouncedSearch(duration: Duration(milliseconds: 500));});useEffect(() {void listener() {debouncedSearch.run(() {// 這里調用接口_performSearch(searchController.text);});}searchController.addListener(listener);return () => searchController.removeListener(listener);}, [searchController]);return Padding(padding: const EdgeInsets.all(8.0),child: Column(children: [TextField(controller: searchController,decoration: InputDecoration(hintText: 'Search...',),),Expanded(child: ListView(children: [// 搜索結果展示],),),],),);}void _performSearch(String query) async {if (query.isEmpty) return;try {// 模擬調用接口print('Searching for $query');// 在這里用 Dio 或其他包實際調用接口var response = await Dio().get('https://api.example.com/search', queryParameters: {'q': query});print(response.data);} catch (e) {print('Search error: $e');}}
}class _DebouncedSearch {final Duration duration;Timer? _timer;_DebouncedSearch({required this.duration});void run(void Function() action) {_timer?.cancel();_timer = Timer(duration, action);}
}