成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

針對Web網頁性能管理詳細的解說

開發 前端
你會有什么反應?我猜想,大多數用戶會關閉這個頁面,改為訪問其他網站。作為一個開發者,肯定不愿意看到這種情況,怎樣才能提高性能呢?

你遇到過性能很差的網頁嗎?

這種網頁響應非常緩慢,占用大量的 CPU 和內存,瀏覽起來常常有卡頓,頁面的動畫效果也不流暢。

你會有什么反應?我猜想,大多數用戶會關閉這個頁面,改為訪問其他網站。作為一個開發者,肯定不愿意看到這種情況,怎樣才能提高性能呢?

本文將詳細介紹性能問題的出現原因,以及解決方法。

一、網頁生成的過程

要理解網頁性能為什么不好,就要了解網頁是怎么生成的。

網頁的生成過程,大致可以分成五步。

  • HTML 代碼轉化成 DOM

  • CSS 代碼轉化成 CSSOM(CSS Object Model)

  • 結合 DOM 和 CSSOM,生成一棵渲染樹(包含每個節點的視覺信息)

  • 生成布局(layout),即將所有渲染樹的所有節點進行平面合成

  • 將布局繪制(paint)在屏幕上

這五步里面,第一步到第三步都非常快,耗時的是第四步和第五步。

“生成布局”(flow)和”繪制”(paint)這兩步,合稱為”渲染”(render)。

二、重排和重繪

網頁生成的時候,至少會渲染一次。用戶訪問的過程中,還會不斷重新渲染。

以下三種情況,會導致網頁重新渲染。

  • 修改 DOM

  • 修改樣式表

  • 用戶事件(比如鼠標懸停、頁面滾動、輸入框鍵入文字、改變窗口大小等等)

重新渲染,就需要重新生成布局和重新繪制。前者叫做”重排”(reflow),后者叫做”重繪”(repaint)。

需要注意的是,”重繪”不一定需要”重排”,比如改變某個網頁元素的顏色,就只會觸發”重繪”,不會觸發”重排”,因為布局沒有改變。但是,”重排”必然導致”重繪”,比如改變一個網頁元素的位置,就會同時觸發”重排”和”重繪”,因為布局改變了。

#p#

三、對于性能的影響

重排和重繪會不斷觸發,這是不可避免的。但是,它們非常耗費資源,是導致網頁性能低下的根本原因。

提高網頁性能,就是要降低”重排”和”重繪”的頻率和成本,盡量少觸發重新渲染。

前面提到,DOM 變動和樣式變動,都會觸發重新渲染。但是,瀏覽器已經很智能了,會盡量把所有的變動集中在一起,排成一個隊列,然后一次性執行,盡量避免多次重新渲染。

  1. div.style.color = 'blue'
  2. div.style.marginTop = '30px'

上面代碼中,div 元素有兩個樣式變動,但是瀏覽器只會觸發一次重排和重繪。

如果寫得不好,就會觸發兩次重排和重繪。

  1. div.style.color = 'blue'
  2. var margin = parseInt (div.style.marginTop); 
  3. div.style.marginTop = (margin + 10) + 'px'

上面代碼對 div 元素設置背景色以后,第二行要求瀏覽器給出該元素的位置,所以瀏覽器不得不立即重排。

一般來說,樣式的寫操作之后,如果有下面這些屬性的讀操作,都會引發瀏覽器立即重新渲染。

  • offsetTop/offsetLeft/offsetWidth/offsetHeight

  • scrollTop/scrollLeft/scrollWidth/scrollHeight

  • clientTop/clientLeft/clientWidth/clientHeight

  • getComputedStyle ()

所以,從性能角度考慮,盡量不要把讀操作和寫操作,放在一個語句里面。

  1. // bad 
  2. div.style.left = div.offsetLeft + 10 + "px"
  3. div.style.top = div.offsetTop + 10 + "px"
  4. // good 
  5. var left = div.offsetLeft; 
  6. var top  = div.offsetTop; 
  7. div.style.left = left + 10 + "px"
  8. div.style.top = top + 10 + "px"

一般的規則是:

  1. 樣式表越簡單,重排和重繪就越快。

  2. 重排和重繪的 DOM 元素層級越高,成本就越高。

  3. table 元素的重排和重繪成本,要高于 div 元素

四、提高性能的九個技巧

有一些技巧,可以降低瀏覽器重新渲染的頻率和成本。

第一條是上一節說到的,DOM 的多個讀操作(或多個寫操作),應該放在一起。不要兩個讀操作之間,加入一個寫操作。

第二條,如果某個樣式是通過重排得到的,那么最好緩存結果。避免下一次用到的時候,瀏覽器又要重排。

第三條,不要一條條地改變樣式,而要通過改變 class,或者 csstext 屬性,一次性地改變樣式。

  1. // bad 
  2. var left = 10
  3. var top = 10
  4. el.style.left = left + "px"
  5. el.style.top  = top  + "px"
  6.  
  7. // good 
  8. el.className += " theclassname"
  9.  
  10. // good 
  11. el.style.cssText += "; left: " + left + "px; top: " + top + "px;"

第四條,盡量使用離線 DOM,而不是真實的網面 DOM,來改變元素樣式。比如,操作 Document Fragment 對象,完成后再把這個對象加入 DOM。再比如,使用 cloneNode () 方法,在克隆的節點上進行操作,然后再用克隆的節點替換原始節點。

第五條,先將元素設為 display: none (需要 1 次重排和重繪),然后對這個節點進行 100 次操作,最后再恢復顯示(需要 1 次重排和重繪)。這樣一來,你就用兩次重新渲染,取代了可能高達 100 次的重新渲染。

第六條,position 屬性為 absolute 或 fixed 的元素,重排的開銷會比較小,因為不用考慮它對其他元素的影響。

第七條,只在必要的時候,才將元素的 display 屬性為可見,因為不可見的元素不影響重排和重繪。另外,visibility : hidden 的元素只對重排有影響,不影響重繪。

第八條,使用虛擬 DOM 的腳本庫,比如 React 等。

第九條,使用 window.requestAnimationFrame ()、window.requestIdleCallback () 這兩個方法調節重新渲染(詳見后文)。

#p#

五、刷新率

很多時候,密集的重新渲染是無法避免的,比如 scroll 事件的回調函數和網頁動畫。

網頁動畫的每一幀(frame)都是一次重新渲染。每秒低于 24 幀的動畫,人眼就能感受到停頓。一般的網頁動畫,需要達到每秒 30 幀到 60 幀的頻率,才能比較流暢。如果能達到每秒 70 幀甚至 80 幀,就會極其流暢。

大多數顯示器的刷新頻率是 60Hz,為了與系統一致,以及節省電力,瀏覽器會自動按照這個頻率,刷新動畫(如果可以做到的話)。

所以,如果網頁動畫能夠做到每秒 60 幀,就會跟顯示器同步刷新,達到最佳的視覺效果。這意味著,一秒之內進行 60 次重新渲染,每次重新渲染的時間不能超過 16.66 毫秒。

一秒之間能夠完成多少次重新渲染,這個指標就被稱為”刷新率”,英文為 FPS(frame per second)。60 次重新渲染,就是 60FPS。

#p#

六、開發者工具的 Timeline 面板

Chrome 瀏覽器開發者工具的 Timeline 面板,是查看”刷新率”的最佳工具。這一節介紹如何使用這個工具。

首先,按下 F12 打開”開發者工具”,切換到 Timeline 面板。

左上角有一個灰色的圓點,這是錄制按鈕,按下它會變成紅色。然后,在網頁上進行一些操作,再按一次按鈕完成錄制。

Timeline 面板提供兩種查看方式:橫條的是”事件模式”(Event Mode),顯示重新渲染的各種事件所耗費的時間;豎條的是”幀模式”(Frame Mode),顯示每一幀的時間耗費在哪里。

先看”事件模式”,你可以從中判斷,性能問題發生在哪個環節,是 JavaScript 的執行,還是渲染?

不同的顏色表示不同的事件。

  • 藍色:網絡通信和 HTML 解析

  • 黃色:JavaScript 執行

  • 紫色:樣式計算和布局,即重排

  • 綠色:重繪

哪種色塊比較多,就說明性能耗費在那里。色塊越長,問題越大。

幀模式(Frames mode)用來查看單個幀的耗時情況。每幀的色柱高度越低越好,表示耗時少。

你可以看到,幀模式有兩條水平的參考線。

下面的一條是 60FPS,低于這條線,可以達到每秒 60 幀;上面的一條是 30FPS,低于這條線,可以達到每秒 30 次渲染。如果色柱都超過 30FPS,這個網頁就有性能問題了。

此外,還可以查看某個區間的耗時情況。

或者點擊每一幀,查看該幀的時間構成。

#p#

七、window.requestAnimationFrame ()

有一些 JavaScript 方法可以調節重新渲染,大幅提高網頁性能。

其中最重要的,就是 window.requestAnimationFrame () 方法。它可以將某些代碼放到下一次重新渲染時執行。

  1. function doubleHeight (element) { 
  2.   var currentHeight = element.clientHeight; 
  3.   element.style.height = (currentHeight * 2) + 'px'
  4. elements.forEach (doubleHeight); 

上面的代碼使用循環操作,將每個元素的高度都增加一倍。可是,每次循環都是,讀操作后面跟著一個寫操作。這會在短時間內觸發大量的重新渲染,顯然對于網頁性能很不利。

我們可以使用window.requestAnimationFrame (),讓讀操作和寫操作分離,把所有的寫操作放到下一次重新渲染。

  1. function doubleHeight (element) { 
  2.   var currentHeight = element.clientHeight; 
  3.   window.requestAnimationFrame (function () { 
  4.     element.style.height = (currentHeight * 2) + 'px'
  5.   }); 
  6. elements.forEach (doubleHeight); 

頁面滾動事件(scroll)的監聽函數,就很適合用 window.requestAnimationFrame () ,推遲到下一次重新渲染。

  1. $(window) .on ('scroll', function() { 
  2.    window.requestAnimationFrame (scrollHandler); 
  3. }); 

當然,最適用的場合還是網頁動畫。下面是一個旋轉動畫的例子,元素每一幀旋轉 1 度。

  1. var rAF = window.requestAnimationFrame; 
  2.  
  3. var degrees = 0
  4. function update () { 
  5.   div.style.transform = "rotate (" + degrees + "deg)"
  6.   console.log ('updated to degrees ' + degrees); 
  7.   degrees = degrees + 1
  8.   rAF (update); 
  9. rAF (update); 

#p#

八、window.requestIdleCallback ()

還有一個函數 window.requestIdleCallback (),也可以用來調節重新渲染。

它指定只有當一幀的末尾有空閑時間,才會執行回調函數。

  1. requestIdleCallback (fn); 

上面代碼中,只有當前幀的運行時間小于 16.66ms 時,函數 fn 才會執行。否則,就推遲到下一幀,如果下一幀也沒有空閑時間,就推遲到下下一幀,以此類推。

它還可以接受第二個參數,表示指定的毫秒數。如果在指定的這段時間之內,每一幀都沒有空閑時間,那么函數 fn 將會強制執行。

  1. requestIdleCallback (fn, 5000); 

上面的代碼表示,函數 fn 最遲會在 5000 毫秒之后執行。

函數 fn 可以接受一個 deadline 對象作為參數。

  1. requestIdleCallback (function someHeavyComputation (deadline) { 
  2.   while(deadline.timeRemaining () > 0) { 
  3.     doWorkIfNeeded (); 
  4.   } 
  5.  
  6.   if(thereIsMoreWorkToDo) { 
  7.     requestIdleCallback (someHeavyComputation); 
  8.   } 
  9. }); 

上面代碼中,回調函數 someHeavyComputation 的參數是一個 deadline 對象。

deadline 對象有一個方法和一個屬性:timeRemaining () 和 didTimeout。

(1)timeRemaining () 方法

timeRemaining () 方法返回當前幀還剩余的毫秒。這個方法只能讀,不能寫,而且會動態更新。因此可以不斷檢查這個屬性,如果還有剩余時間的話,就不斷執行某些任務。一旦這個屬性等于0,就把任務分配到下一輪requestIdleCallback

前面的示例代碼之中,只要當前幀還有空閑時間,就不斷調用 doWorkIfNeeded 方法。一旦沒有空閑時間,但是任務還沒有全執行,就分配到下一輪requestIdleCallback

(2)didTimeout 屬性

deadline 對象的 didTimeout 屬性會返回一個布爾值,表示指定的時間是否過期。這意味著,如果回調函數由于指定時間過期而觸發,那么你會得到兩個結果。

  • timeRemaining 方法返回0

  • didTimeout 屬性等于 true

因此,如果回調函數執行了,無非是兩種原因:當前幀有空閑時間,或者指定時間到了。

  1. function myNonEssentialWork (deadline) { 
  2.   while ((deadline.timeRemaining () > 0 || deadline.didTimeout) && tasks.length > 0
  3.     doWorkIfNeeded (); 
  4.  
  5.   if (tasks.length > 0
  6.     requestIdleCallback (myNonEssentialWork); 
  7.  
  8. requestIdleCallback (myNonEssentialWork, 5000); 

上面代碼確保了,doWorkIfNeeded 函數一定會在將來某個比較空閑的時間(或者在指定時間過期后)得到反復執行。

requestIdleCallback 是一個很新的函數,剛剛引入標準,目前只有 Chrome 支持。

九、參考鏈接

 

責任編輯:王雪燕 來源: 阮一峰的網絡日志
相關推薦

2015-09-17 10:07:33

2017-09-19 14:28:06

Web開發網站

2010-07-09 10:54:23

SQLServer字段

2009-01-09 22:29:38

服務器虛擬化磁盤陣列

2016-01-05 10:35:04

JavaSpringJavaConfig

2010-02-04 15:01:07

Android架構

2011-06-09 16:03:47

加載速度SEO

2010-02-05 16:58:18

Android服務

2009-12-31 13:56:57

ADO訪問接口

2009-12-21 13:19:34

ADO.NET組件

2011-06-02 12:34:16

正則表達式

2010-02-25 14:50:59

Linux文件系統

2015-09-17 15:23:56

阮一峰網頁性能

2011-05-11 09:47:14

mobl移動web開發

2011-07-20 17:54:02

C++

2010-03-19 10:19:42

第四層交換

2009-12-16 09:56:30

Fedora Gnom

2009-12-31 11:27:33

2010-09-08 13:23:00

2009-12-31 09:54:04

ADO.NET數據訪問
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人一区二区电影 | 成人国产免费视频 | 欧美又大粗又爽又黄大片视频 | 美女视频三区 | 欧美精品乱码99久久影院 | 成人午夜视频在线观看 | 在线视频 亚洲 | 九色国产 | 99久9| 亚洲h在线观看 | 青青草一区| 日韩中文字幕在线不卡 | 国产一区二区在线免费播放 | 国产精品二区三区在线观看 | 操人网站| 欧美一区二区三区高清视频 | 国产亚洲精品成人av久久ww | 美女张开腿露出尿口 | www.一区二区三区 | 久久精品国产一区老色匹 | 国产一区二区三区四区在线观看 | 色免费视频 | 黄色大片视频 | 日韩一二三区视频 | 久久激情视频 | 午夜免费观看体验区 | 亚洲国产精品久久 | 中文字幕精品一区久久久久 | 亚洲男人天堂 | 91九色麻豆 | 日韩视频―中文字幕 | 91精品国产综合久久久久久漫画 | 亚洲精品9999| 在线观看国产视频 | 欧美一区二区 | 亚洲人免费视频 | 精品国产黄a∨片高清在线 成人区精品一区二区婷婷 日本一区二区视频 | 在线欧美| 日韩久久久久 | 在线观看中文字幕dvd播放 | 最新国产福利在线 |