### 我的前言
? ? ? HTML5的API體系是重要知識,但是如何寫出更高效的Web App對于從程序員進階為高級程序員來說更為重要。技很重要,但是容易學會,術才是茫茫人海中鶴立雞群,安生立命之本。
? ? ??碼農們容易嗎?是的,我們必須要不斷努力和學習才能進化為高級碼農乃至頂級碼農。
? ? ??Web App的性能優化非常重要,之前我有過一篇LinkedIn的相關文章《[用HTML5實現iPad應用無限平滑滾動](http://blog.csdn.net/hfahe/article/details/7535914)》。
? ? ??本文從另外一方面揭示了瀏覽器的渲染原理,就像開發Windows客戶端要理解操作系統原理一樣,有時候,性能優化不僅僅是了解HTML、JavaScript、CSS,瀏覽器的實現是墻外的另一角。
? ? ??本文中所講述的原理可以延伸應用到很多平臺,例如如何保證Android應用屏幕滑動時的流暢性?有許多需要注意的點,例如不要在主線程中進行耗時的計算、不要采用過大過多的背景圖片等等。
? ? ??其實這個事情我們完全可以寫一本書來詳細闡述,也許。
……………………………………?……………………………………
### 目錄
- 介紹
- 滾動的原理
- 繪制分析
- 圖像縮放
- 代價昂貴的樣式
- Reflow與重繪
- 滾動事件防抖
- 結論
- 擴展閱讀
### 介紹
? ? ??滾動的性能優化看起來不是一件很有必要的事情。畢竟,內容已有樣式,所有資源已經或正在加載,我們為什么要關心滾動時會發生些什么呢?其實原因很簡單,只要你滾動,瀏覽器就會把你的網站或者App繪制到屏幕上。這意味著我們有機會盡量減少瀏覽器的工作來最大限度地提高頁面性能。
? ? ??當用戶使用你的應用時,平滑滾動實際上是用戶體驗里經常被忽視的重要組成部分。當滾動體驗流暢時,使用這個應用會感覺到如絲般光滑和愉悅的體驗,而卡頓則讓人難以忍受。
### 滾動的原理
? ? ??讓我們來看看在滾動時有什么事情發生。要理解這點,我們必須簡單介紹一下瀏覽器如何在屏幕上繪制內容。一切都始于DOM樹,它實際上是頁面內的所有元素。瀏覽器查看帶樣式的DOM樹,找出滾動時它認為看起來是一樣的元素。然后,它把這些元素組合在一起,并且為它們生成一張圖片,這就是所謂的層。每一層需要被繪制和柵格化為一種結構,然后合成在一起,這就是你在屏幕上看到的圖像。
? ? ??如果你有對Chrome渲染的詳細信息感興趣,可以看看這篇[概要](http://www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome)(宇捷:在國內可以看看[這篇](http://www.docin.com/p-437050924.html))。
? ? ??當你滾動頁面時,瀏覽器可能會需要繪制這些層(有時也被稱為合成層)里的一些像素。通過元素分組,當某個層的內容改變時,我們只需要更新該層的結構,并僅僅重繪和柵格化渲染層結構里變化的那一部分,而無需完全重繪。顯然,如果當你滾動時,像視差網站(宇捷:關于視差網站,可以看看[這篇文章](http://www.html5rocks.com/en/tutorials/speed/parallax/),以及[示例網站1](http://www.rowtothepole.com/)、[示例網站2](http://www.adidas.com/com/apps/snowboarding/)、[示例網站3](http://www.bbc.co.uk/news/entertainment-arts-20026367),稍后我可能會介紹這種設計方式)這樣有東西在移動時,有可能在多層導致大面積的內容調整,這會導致大量的繪制工作。
? ? ??總之,**少繪最佳**。
### 繪制分析
? ? ??理論說了這么多,讓我們來看看實踐中如何工作。你可以用Chrome瀏覽器的DevTools來看看這個[演示頁面](http://www.html5rocks.com/static/demos/scrolling/demo.html)。如果你打開??時間軸面板(Timeline panel),設置為幀模式(frame mode),點擊紀錄按鈕(record button),然后開始滾動,你可以看到一堆綠色的條。我錄制了一段[視頻](http://www.youtube.com/embed/KCtOt9OXvAM?rel=0)來展示你應該做什么和可以看到什么(宇捷:甚至在切換Tab時你也可以看見圖形在變化)。
? ? ??在時間線下方的列表中,你會看到全部繪制記錄。這是Chrome繪制和柵格化頁面合成層結構的過程。(所有繪制完成后,你還可以看到一些合成層的記錄,這是所有這些已經更新的層為將在屏幕上顯示進行合成準備)

Chrome DevTools里的繪制記錄
? ? ??在每一個繪制記錄后,你可以看到繪制區域的尺寸,如果你滾動頁面,Chrome將突出顯示頁面中需要重繪的區域。另一個查看重繪區域的方法是,通過右下角的小齒輪圖標進入DevTools設置,在里面激活 “顯示繪制矩形(show paint rectangles)”選項。這是我們得到的滾動時頁面性能的第一個指標。提醒一下:你應該尋找最小的可能繪制區域。
? ? ??在頁面左側菜單可見的情況下,繪制區域基本上是整個屏幕,性能會受到很大影響。原因是這時Chrome創建了多個需調整的區域。在這種情況下,整個橫向的可視內容會進入視圖,還有100%高的左側菜單也會進入同一個合成層,所有這一切會合成為一個100%高、100%寬的區域。如果你使用頁面上方的勾選框禁用側邊欄菜單,再次滾動,你會發現此時只需要重繪橫向的細條,這會快上很多。
? ? ??如果你想看看實際的例子,試試打開[Google+](https://plus.google.com/),然后把側邊欄導航的樣式從position: fixed修改為position: absolute。當然這會改變站點的行為,但是關鍵是通過切換風格,你可以看到真正性能的提升。你對此的反應可能是考慮以后應不應該使用position:fixed,但實際上所有這一切都依賴于環境和需求。最重要的是要能夠測量和了解你的決定所帶來的影響。
### 圖像縮放
? ? ??在基本的繪制記錄外我們還得到了一些信息,尤其是和圖像相關。?如果在記錄時縮放頁面,你會看到一些繪制記錄允許展開來查看更多信息。這么做之后,你應該可以看到一些圖像縮放記錄。這正是它所揭示的:瀏覽器需要把縮放圖像作為繪制工作的一部分。

Chrome DevTools的圖像縮放記錄
? ? ??如果用CSS或者圖像尺寸屬性縮放了一張大圖,你就更有可能看到這樣的情況。?當然,瀏覽器重新縮放圖像的次數,以及它需要這樣做的頻率,會影響頁面的性能,因為它們發生在瀏覽器主線程,由此會阻塞其它任務。
? ? ??因此,使用無需縮放的圖像是非常重要的。可以從一個有趣的側面說明這一點,縮放操作很多情況下是不可避免的,尤其是在移動設備上。在這種情況下你沒有太多可以做的事情,但是你最好知道這會(并且極有可能)發生。有利的一面是,瀏覽器總是在改進中,尤其渲染更是一個熱門的話題,所以你可以期望事情在未來的幾個月內會變得更好。
? ? ??因此綜上所述,還有其它可能會影響滾動性能的因素:
- 代價昂貴的樣式
- Reflow與重繪
- 滾動事件去抖
? ? ??讓我們來詳細談談每一點。
### 代價昂貴的樣式
? ? ??首先要說的是,并非所有的樣式都是一樣的,一些效果例如盒陰影(box-shadow)和oft-quoted代價特別昂貴。原因很簡單,因為它們相關的繪制代碼比其它樣式需要更長的時間來運行。這意味著,如果你使用了這些樣式,而且需要經常重繪,你很可能遇到性能問題。第二點要說的是一切都會變化,所以現在速度慢的樣式可以被優化,以后變得很快,并且瀏覽器間還有很大不同。這里的關鍵是像上面一樣利用Chrome的開發者工具來確定其中的瓶頸來自哪里,然后看看我們如何可以減少瀏覽器的負載。
? ? ??2012年[谷歌I / O大會上](https://developers.google.com/events/io/)Nat Duca和Tom Wiltzius做了一場關于Chrome如何進行渲染優化的[精彩演講](http://www.youtube.com/embed/hAzhayTnhEI?rel=0),其中提到了一大堆寶貴的經驗,包括代價昂貴的樣式以及如何衡量其影響。
### Reflow與重繪
? ? ??當用JavaScript請求一個元素的`offsetTop`屬性時,你相當于馬上給瀏覽器分配了大量的工作,因為它需要去進行頁面布局來計算正確答案。這個過程被稱為Reflow。如果我們基于offsetTop值再改變了此元素的其它屬性,它將需要重新繪制(會影響到合成層),這個代價可能非常昂貴。它還具有連鎖效應,重繪會導致`offsetTop`計算失效,因為該元素發生了變化。
? ? ??真正的問題來自于元素集合。如果我們在重繪后馬上計算每個元素的位置,我們會迫使瀏覽器進入一個代價昂貴的、完全不必要的Reflow-重繪循環。在這種情況下,我們需要做的是分兩次來限制避免這個循環:第一次我們簡單的獲取`offsetTop`值,而在第二次里進行視覺更新。通過這種方式,我們避免了需要反復重新計算元素在頁面中的位置,并且假定我們使用`requestAnimationFrame`?,我們將為瀏覽器安排在最佳時機進行視覺更新。
? ? ??值得一提的是除了offsetTop之外還有[其它操作](http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html)會導致Reflow操作發生,所以必須要小心檢查所有相關內容。
### 滾動事件防抖
? ? ??如果你正在構建一個大的視差滾動(`Parallax?Scrolling`)網站,你自然傾向于在觸發滾動事件時進行視覺更新。這里的主要問題是,滾動事件沒有限制瀏覽器視覺更新的時間,比如在一個`requestAnimationFrame`回調里。所以在一個獨立的渲染幀里會存在執行多個更新的風險。如果視覺更新代價巨大,而你正在開發一個視差滾動類似的網站(有大量區域調整,大量繪制和合成),頻繁而不是僅必需的視覺更新無疑是一個壞主意。要解決這個問題,你需要將滾動事件進行防抖處理。你只需要在收到滾動事件時簡單的存儲最后一個滾動值,然后在`requestAnimationFrame里`執行視覺更新時,使用這個值。這意味著瀏覽器可以在正確的時刻安排視覺更新,我們在每幀中僅完成絕對必要的工作。
? ? ??我們在[前面的文章](http://www.html5rocks.com/en/tutorials/speed/animations/)討論過Reflow-重繪循環,如果你想了解更多時可以看看。
### 結論
? ? ??現在你知道如何使用Chrome DevTools的時間軸來分析應用的滾動性能了。值得重申的是,性能特性總是在改變,因此有些方式目前速度比其它更快,但這會隨時間而改變。關鍵是理解如何認識上面讀到的內容,這樣你可以相應調整你的實現方式。
? ? ??你應該經常使用開發者工具檢查**實際的**性能數據。靠眼睛來判斷可能很簡單,但是結果沒那么直接。所以,不要去揣測它,而應該去測試它。
### 擴展閱讀
? ? ??如果你對瀏覽器實現感興趣,或者想了解更多技巧來避免渲染性能的瓶頸,可以看看下面這些文章。
1. [Tali Garsiel & Paul Irish on how browsers work](http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/):瀏覽器如何工作
1. [A summary of hardware compositing in Chrome](http://www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome):Chrome硬件合成概要
1. [Paul Lewis on debouncing scroll events and reflow / repaint cycles](http://www.html5rocks.com/en/tutorials/speed/animations/):滾動時間防抖和Reflow-重繪循環
1. [Tom Wiltzius on jank busting for better rendering performance](http://www.html5rocks.com/en/tutorials/speed/rendering/):更好渲染的性能
? ? ??此網頁內容的授權是[知識共享許可3.0](http://creativecommons.org/licenses/by/3.0/)協議,代碼授權是[Apache 2.0協議](http://www.apache.org/licenses/LICENSE-2.0)。
? ? ??譯自:[HTML5Rocks](http://www.html5rocks.com/en/tutorials/speed/scrolling/)譯者:[蔣宇捷](http://blog.csdn.net/hfahe)轉載請注明
- 前言
- AutoPager的簡單實現
- 利用CSS3特性巧妙實現漂亮的DIV箭頭
- IE9在Win7下任務欄新特性簡介
- 瀏覽器九宮格的簡單實現
- Raphael js庫簡介
- 使用CSS3構建Ajax加載動畫
- 用CSS3創建動畫價格表
- 用CSS3實現瀏覽器的縮放功能
- 用純CSS3實現QQ LOGO
- 用CSS3創建旋轉載入器
- 使用Javascript開發移動應用程序
- 用HTML5創建超酷圖像灰度漸變效果
- 使用CSS3創建文字顏色漸變(CSS3 Text Gradient)
- 僅用CSS創建立體旋轉幻燈片
- 如何創建跨瀏覽器的HTML5表單
- 用CSS3實現動畫進度條
- HTML5 Guitar Tab Player
- 奇妙的HTML5 Canvas動畫實例
- 談HTML5和CSS3的國際化支持
- 實現跨瀏覽器的HTML5占位符
- 前端開發必備工具:WhatFont Bookmarklet-方便的查詢網頁上的字體
- 使用HTML5和CSS3來創建幻燈片
- HTML5之美
- 如何使用HTML5創建在線精美簡歷
- 以小見大、由淺入深-談如何面試Javascript工程師
- 快速入門:HTML5強大的Details元素
- 用CSS3實現圖像風格
- HTML5視頻字幕與WebVTT
- 用純CSS3實現Path華麗動畫
- 用3個步驟實現響應式網頁設計
- 遇見CSS3濾鏡
- 關于CSS3濾鏡的碎念
- 用純CSS3繪制萌系漫畫人物動態頭像
- CSS3新的鼠標樣式介紹
- 用HTML5獻上愛的3D玫瑰
- 對HTML5 Device API相關規范的解惑
- 如何使用HTML5實現拍照上傳應用
- 2012第一季度國外HTML5移動開發趨勢
- HTML5新特性:范圍樣式
- 百度開發者大會-《用HTML5新特性開發移動App》PPT分享
- Chrome 19對于HTML5最新支持的動態:電池狀態API,全屏API,震動API,語音API
- 遇見Javascript類型數組(Typed Array)
- 用HTML5 Audio API開發游戲音樂
- 用HTML5實現人臉識別
- 用Javascript實現人臉美容
- Chrome 20對于HTML5最新支持的動態:顏色輸入,網絡信息API,CSS著色器
- 用HTML5實現手機搖一搖的功能
- 用HTML5實現iPad應用無限平滑滾動
- 用非響應式設計構建跨端Web App
- 了解SVG
- HTML5圖像適配介紹
- HTML5安全:內容安全策略(CSP)簡介
- HTML5安全:CORS(跨域資源共享)簡介
- 用CSS3 Region和3D變換實現書籍翻頁效果
- 談談移動App的思維誤區
- Chrome新特性:文件夾拖拽支持
- 《關注HTML5安全》
- HTML5安全風險詳析之一:CORS攻擊
- HTML5安全風險詳析之二:Web Storage攻擊
- HTML5圖像適配最新進展:響應式圖片規范草案
- HTML5移動Web App相關標準狀態及路線圖
- HTML5安全風險詳析之三:WebSQL攻擊
- Chrome引入WebRTC支持視頻聊天App
- HTML5安全風險詳析之四:Web Worker攻擊
- HTML5安全風險詳析之五:劫持攻擊
- HTML5安全風險詳析之六:API攻擊
- HTML5安全攻防詳析之七:新標簽攻擊
- 在iOS Safari中播放離線音頻
- 使用WebRTC實現遠程屏幕共享
- Firefox、Android、iOS遇見WebRTC
- HTML5光線傳感器簡介
- HTML5安全攻防詳析之八:Web Socket攻擊
- HTML5安全攻防詳析之完結篇:HTML5對安全的改進
- 激動人心!在網頁上通過語音輸入文字 - HTML5 Web Speech API介紹
- Web滾動性能優化實戰
- 用CSS3設計響應式導航菜單
- 用HTML5構建高性能視差網站
- 漫談@supports與CSS3條件規則
- HTML5下載屬性簡介
- 如何開發優秀的HTML5游戲?-迪斯尼《尋找奧茲之路》游戲技術詳解(一)
- 如何開發優秀的HTML5游戲?-迪斯尼《尋找奧茲之路》游戲技術詳解(二)
- 趨勢:Chrome為打包應用提供強大新特性
- 從HTML5移動應用現狀談發展趨勢
- 基于HTML5的Web跨設備超聲波通信方案