关于前端性能优化问题,认识网页加载过程和防抖节流
前端性能優化—網頁加載過程、性能優化方法、防抖和節流
- 一、網頁加載過程
- 1、加載資源的形式
- 2、加載資源的過程
- 3、渲染頁面的過程
- 4、關于window.onload 和 DOMContentLoaded
- 二、性能優化
- 1、性能優化原則
- 2、性能優化的方法
- 3、讓加載更快
- 4、讓渲染更快
- 三、防抖和節流
- 1、防抖 debounce
- 2、節流 throttle
- 四、寫在最后
平常我們在加載網頁的時候,首先需要先加載網頁代碼,之后渲染出頁面,在這個期間會執行若干個 JS 。那么,如果想要讓網頁呈現速度和渲染速度快,我們就得保證我們的代碼在瀏覽器這個運行環境當中穩定且高效。這就談到一個前端性能優化的問題。
在下面這篇文章,將講解關于前端性能優化的一些常見問題。
一、網頁加載過程
1、加載資源的形式
網頁需要加載的資源,一般包括以下內容:
- html 代碼;
- 媒體文件,如圖片、視頻等;
- javascript、 css 。
2、加載資源的過程
加載資源的過程需要經過以下幾個步驟:
- DNS 解析:域名 -> IP 地址。
- 瀏覽器根據 IP 地址向服務器發起 http 請求。
- 服務器處理 http 請求,并返回給瀏覽器。
3、渲染頁面的過程
渲染過程 - 1
- 根據 HTML 代碼生成 DOM Tree ;
- 根據 CSS 代碼生成 CSSOM ;
- 將 DOM Tree 和 CSSOM 整合形成 Render Tree 。
渲染過程 - 2
- 根據 Render Tree 渲染頁面;
- 遇到 <script> 則暫停渲染,優先加載并執行 JS 代碼,完成后再繼續執行;
- 直至把 Render Tree 渲染完成。
4、關于window.onload 和 DOMContentLoaded
window.addEventListener('load', function(){//頁面的全部資源加載完才會執行,包括圖片、視頻等 });document.addEventListener('DOMContentLoaded', function(){//DOM 渲染完即可執行,此時圖片、視頻還可能沒加載完 -> 盡量選擇此方法 });二、性能優化
性能優化是一個綜合性的問題,永遠沒有標準答案,下面將從幾個方面來講解性能優化的內容。
1、性能優化原則
- 多使用內存、緩存或其他方法。
- 減少 CPU 計算量,減少網絡加載耗時。
- 適用于所有編程的優化方法 —— 空間換時間。
2、性能優化的方法
(1) 讓加載更快
(2) 讓渲染更快
3、讓加載更快
(1)減少資源體積
- 壓縮代碼: 可以通過壓縮代碼來減少資源體積,包括 js 文件、 css 文件和圖片都可以進行壓縮。同時服務器端 可以通過 gzip 算法來對資源進行壓縮。
(2)減少訪問次數
-
合并代碼: 比如說我們寫了三四個文件,但通過打包可能就只剩下一個文件,并且文件里面是以一行的形式出現,或者雪碧圖也算是其中一種方式。
-
SSR服務器端渲染:
服務端把網頁和數據一起加載,一起渲染。
非SSR:先加載網頁,再加載數據,再渲染數據,這個過程聽起來就優點繁瑣。
早期的 JSP 、ASP 、PHP,現在的 vue SSR 、React SSR。
-
緩存:
舉個例子:假設有 10 個資源,如果每次請求都要請求 10 次,那這樣子是非常耗時的;那如果 10 個資源中有 6 個命中了緩存,則只需要請求另外 4 個。
那如何做緩存來達到減少訪問次數呢?
前端會在靜態資源上加 hash 后綴,根據文件內容計算 hash 。當文件內容不變時,則 hash 和 url 都不變。此時 url 和文件不變,則會自動觸發 http 緩存機制,返回 304 。
(3)使用更快的網絡
-
通過 CDN 來操作:
CDN ,即內容分發網絡(Content Delivery Network,簡稱 CDN ),是建立并覆蓋在承載網之上,由分布在不同區域的邊緣節點服務器群組成的分布式網絡。
CDN 就是一個反向代理,根據地域來做網絡服務,主要是前端用來做靜態資源加載,通常在前端項目部署的時候進行這項操作。
我們平常所使用的 bootstrap 、 JQuery 一般都采用 cdn 的形式。
這里我也不是特別了解 cdn 的內容,只能才疏學淺的進行介紹。附上一篇我看完覺得比較好理解的文章,大家可以根據需求進行查看~
4、讓渲染更快
(1)html、css、js和圖片層面
-
css 放在 head , JS 放在 body 最下面;
-
盡早開始執行 JS ,用 DOMContentLoaded 觸發;
-
懶加載(圖片懶加載,上滑加載更多)。
<img id = "img1" src = "preview.png" data-realsrc = "abe.png"/> <script type = "text/javascript">let img1 = document.getElementById('img1');//把真實的圖片data-realsrc賦值給預覽的圖片srcimg1.src = img1.getAttribute('data-realsrc');//…… 一系列邏輯操作 </script>
(2)從DOM層面
- 對 DOM 查詢進行緩存;
- 從頻繁進行 DOM 操作,變為合并到一起進行 DOM 結構插入;
注:關于DOM的性能優化我有在之前的一篇文章中講過,在主題一的第4小點,這里不再進行細講,大家可以根據自身需求進行查閱~
(3)防抖 debounce 和 節流 throttle
關于防抖和節流的講解,詳細見下方。
三、防抖和節流
1、防抖 debounce
(1)含義: 從頻繁執行觸發變為只在最后一次觸發。
(2)發生場景: 監聽一個輸入框,如果直接用 keyup 事件,則每輸入一個文字都會觸發 onchange 事件,頻繁執行操作。
(3)防抖解決: 用戶輸入結束或暫停時,才會觸發 change 事件。
(4)引例闡述
假設我們現在要監聽一個輸入框的值,此時我們直接用 keyup 事件來實現。實現方式如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body><input type="text" id="input1"><script type="text/javascript">const input1 = document.getElementById('input1');input1.addEventListener('keyup', function(){console.log(input1.value);});</script> </body> </html>此時瀏覽器的顯示效果是這樣的。
大家可以發現,當我們使用 keyup 事件處理,每當我們輸入一個字母時,瀏覽器就會頻繁進行打印,這樣看起來也太耗費性能了。
于是就有了防抖的解決方案,防抖通過對頻繁執行的操作變為只在最后一次執行。實現方式如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body><input type="text" id="input1"><script type="text/javascript">const input1 = document.getElementById('input1');let timer = null;input1.addEventListener('keyup', function(){if(timer){clearTimeout(timer);}timer = setTimeout(() => {//模擬觸發 change 事件console.log(input1.value);//清空定時器timer = null;}, 500);// console.log(input1.value);});</script> </body> </html>此時瀏覽器的顯示效果是這樣的。
通過上圖發現,當我們在輸入框輸入字母時,只在最后輸入結束時控制臺才打印結果出來。而不會像上面那樣頻繁打印,因此通過防抖,實現了把頻繁執行變為了只在最后一次執行的操作。
(5)封裝防抖函數
通過上面的例子,相信大家對防抖有了一個基礎的了解。接下來我們通過封裝函數來實現相同效果的防抖功能。具體代碼如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body><input type="text" id="input1"><script type="text/javascript">const input1 = document.getElementById('input1');// 封裝一個防抖函數function debounce(fn, delay = 500){// timer 是在閉包中的let timer = null;return function(){if(timer){clearTimeout(timer);}timer = setTimeout(() => {fn.apply(this, arguments);}, delay);}}//運用防抖函數實現input輸入框觸發操作input1.addEventListener('keyup', debounce(function () {console.log(input1.value);}, 600));</script> </body> </html>2、節流 throttle
(1)含義: 從頻繁執行觸發變為每隔一段時間觸發一次。
(2)發生場景: 拖拽一個元素時,直接用 drag 事件,則會頻繁觸發,很容易導致卡頓。
(3)節流解決: 無論拖拽速度多快,都會每隔 100ms 觸發一次(這里的 100ms 不是固定值,也可設置成其它的值)。
(4)引例闡述
假設我們現在要拖拽一個元素,并且想要隨時拿到該元素被拖拽的值。此時我們直接用 drag 事件來實現。實現方式如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>#div1{border: 1px solid #ccc;width: 200px;height: 100px;}</style> </head> <body><div id = "div1" draggable="true">可拖拽</div><script type="text/javascript">const div1 = document.getElementById('div1');//用節流之前div1.addEventListener('drag', function(e) {console.log(e.offsetX, e.offsetY);})</script> </body> </html>此時瀏覽器的顯示效果是這樣的。
大家可以發現,當我們使用 drag 事件處理,每當我們拖拽元素時,瀏覽器就會頻繁進行打印,這樣似乎有一點點耗費性能。
于是就有了節流的解決方案,節流通過對頻繁執行的操作變為只在每隔一段時間操作。實現方式如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>#div1{border: 1px solid #ccc;width: 200px;height: 100px;}</style> </head> <body><div id = "div1" draggable="true">可拖拽</div><script type="text/javascript">const div1 = document.getElementById('div1');// 用節流之后let timer = null;div1.addEventListener('drag', function(e){if(timer){return;}timer = setTimeout(() => {console.log(e.offsetX, e.offsetY);timer = null;}, 500);});</script> </body> </html>此時瀏覽器的顯示效果是這樣的。
通過上圖發現,當我們在拖拽時,不再像剛剛那樣頻繁打印,而是有規律的每隔 500ms 打印一次,這樣子看起來就比剛剛的頻繁觸發要好很多了。所以,節流是通過將頻繁執行改為每隔一段時間執行。
(5)封裝節流函數
通過上面的例子,相信大家對節流有了一個基礎的了解。接下來我們通過封裝函數來實現相同效果的節流功能。具體代碼如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body><input type="text" id="input1"><script type="text/javascript">const input1 = document.getElementById('input1');// 封裝一個節流函數function throttle(fn, delay = 500){let timer = null;return function (){if(timer){return;}timer = setTimeout(() => {fn.apply(this, arguments);timer = null;}, delay);}}//運用節流函數實現拖拽時每隔一段時間觸發操作div1.addEventListener( 'drag', throttle(function(e){console.log(e.offsetX, e.offsetY);}, 200));</script> </body> </html>四、寫在最后
關于前端性能優化的一些基礎內容就講到這里啦!如有疑問或文章有講的不好的地方歡迎評論區評論或私信我交流~
-
關注公眾號 星期一研究室 ,不定期分享學習干貨
-
如果這篇文章對你有用,記得點個贊加個關注再走哦~
總結
以上是生活随笔為你收集整理的关于前端性能优化问题,认识网页加载过程和防抖节流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CAD怎么调整线条线宽
- 下一篇: 『软件工程9』结构化系统分析——解决软件