[TOC]
> **首先說明,這里討論的是 WebKit,描述的是 Chrome 的實現細節,而并非是 web 平臺的功能,因此這里介紹的內容不一定適用于其他瀏覽器。**
> * Chrome 擁有兩套不同的渲染路徑(rendering path):硬件加速路徑和舊軟件路徑(older software path)
> * Chrome 中有不同類型的層: RenderLayer(負責 DOM 子樹)和GraphicsLayer(負責 RenderLayer的子樹),只有 GraphicsLayer 是作為紋理(texture)上傳給GPU的。
> * 什么是紋理?可以把它想象成一個從主存儲器(例如 RAM)移動到圖像存儲器(例如 GPU 中的 VRAM)的位圖圖像(bitmapimage)
> * Chrome 使用紋理來從 GPU上獲得大塊的頁面內容。通過將紋理應用到一個非常簡單的矩形網格就能很容易匹配不同的位置(position)和變形(transformation)。這也就是3DCSS 的工作原理,它對于快速滾動也十分有效。
# 渲染原理
一個 Web 頁面的展示,簡單來說可以認為經歷了以下下幾個步驟。

* JavaScript:一般來說,我們會使用 JavaScript 來實現一些視覺變化的效果。比如做一個動畫或者往頁面里添加一些 DOM 元素等。
* Style:計算樣式,這個過程是根據 CSS 選擇器,對每個 DOM 元素匹配對應的 CSS 樣式。這一步結束之后,就確定了每個 DOM 元素上該應用什么 CSS 樣式規則。
* Layout:布局,上一步確定了每個 DOM 元素的樣式規則,這一步就是具體計算每個 DOM 元素最終在屏幕上顯示的大小和位置。web 頁面中元素的布局是相對的,因此一個元素的布局發生變化,會聯動地引發其他元素的布局發生變化。比如,`<body>`元素的寬度的變化會影響其子元素的寬度,其子元素寬度的變化也會繼續對其孫子元素產生影響。因此對于瀏覽器來說,布局過程是經常發生的。
* Paint:繪制,本質上就是填充像素的過程。包括繪制文字、顏色、圖像、邊框和陰影等,也就是一個 DOM 元素所有的可視效果。一般來說,這個繪制過程是在多個層上完成的。
* Composite:渲染層合并,由上一步可知,對頁面中 DOM 元素的繪制是在多個層上進行的。在每個層上完成繪制過程之后,瀏覽器會將所有層按照合理的順序合并成一個圖層,然后顯示在屏幕上。對于有位置重疊的元素的頁面,這個過程尤其重要,因為一旦圖層的合并順序出錯,將會導致元素顯示異常。

在 Chrome 中其實有幾種不同的層類型:
* RenderLayers 渲染層,這是負責對應 DOM 子樹
* GraphicsLayers 圖形層,這是負責對應 RenderLayers子樹。
在瀏覽器渲染流程中提到了composite概念,在 DOM 樹中每個節點都會對應一個 LayoutObject,當他們的 LayoutObject 處于相同的坐標空間時,就會形成一個 RenderLayers ,也就是渲染層。RenderLayers 來保證頁面元素以正確的順序合成,這時候就會出現層合成(composite),從而正確處理透明元素和重疊元素的顯示。
某些特殊的渲染層會被認為是合成層(Compositing Layers),合成層擁有單獨的 GraphicsLayer,而其他不是合成層的渲染層,則和其第一個擁有 GraphicsLayer 父層公用一個。
而每個GraphicsLayer(合成層單獨擁有的圖層) 都有一個 GraphicsContext,GraphicsContext 會負責輸出該層的位圖,位圖是存儲在共享內存中,作為紋理上傳到 GPU 中,最后由 GPU 將多個位圖進行合成,然后顯示到屏幕上。
# 如何變成合成層
* 3D 或透視變換(perspective transform) CSS 屬性
* 使用加速視頻解碼的 元素 擁有 3D
* (WebGL) 上下文或加速的 2D 上下文的 元素
* 混合插件(如 Flash)
* 對自己的 opacity 做 CSS動畫或使用一個動畫變換的元素
* 擁有加速 CSS 過濾器的元素
* 元素有一個包含復合層的后代節點(換句話說,就是一個元素擁有一個子元素,該子元素在自己的層里)
* 元素有一個z-index較低且包含一個復合層的兄弟元素(換句話說就是該元素在復合層上面渲染)
# 合成層的優點
一旦renderLayer提升為了合成層就會有自己的繪圖上下文,并且會開啟硬件加速,有利于性能提升,里面列舉了一些特點
* 合成層的位圖,會交由 GPU 合成,比 CPU 處理要快
* 當需要 repaint 時,只需要 repaint 本身,不會影響到其他的層
* 對于 transform 和 opacity 效果,不會觸發 layout 和 paint
**注意:**
1. 提升到合成層后合成層的位圖會交GPU處理,但請注意,僅僅只是合成的處理(把繪圖上下文的位圖輸出進行組合)需要用到GPU,生成合成層的位圖處理(繪圖上下文的工作)是需要CPU。
2. 當需要repaint的時候可以只repaint本身,不影響其他層,但是paint之前還有style, layout,那就意味著即使合成層只是repaint了自己,但style和layout本身就很占用時間。
3. 僅僅是transform和opacity不會引發layout 和paint,那么其他的屬性不確定。
總結合成層的優勢:一般一個元素開啟硬件加速后會變成合成層,可以獨立于普通文檔流中,改動后可以避免整個頁面重繪,提升性能。
**性能優化點:**
1. 提升動畫效果的元素 合成層的好處是不會影響到其他元素的繪制,因此,為了減少動畫元素對其他元素的影響,從而減少paint,我們需要把動畫效果中的元素提升為合成層。`提升合成層的最好方式是使用 CSS 的 will-change屬性。從上一節合成層產生原因中,可以知道 will-change 設置為opacity、transform、top、left、bottom、right 可以將元素提升為合成層。`
2. 使用 transform 或者 opacity 來實現動畫效果, 這樣只需要做合成層的合并就好了。
3. 減少繪制區域 對于不需要重新繪制的區域應盡量避免繪制,以減少繪制區域,比如一個 fix 在頁面頂部的固定不變的導航header,在頁面內容某個區域 repaint 時,整個屏幕包括 fix 的 header 也會被重繪。`而對于固定不變的區域,我們期望其并不會被重繪,因此可以通過之前的方法,將其提升為獨立的合成層。減少繪制區域,需要仔細分析頁面,區分繪制區域,減少重繪區域甚至避免重繪。`
# 利用合成層可能踩到的坑
1. 合成層占用內存的問題
2. 層爆炸,由于某些原因可能導致產生大量不在預期內的合成層,雖然有瀏覽器的層壓縮機制,但是也有很多無法進行壓縮的情況,這就可能出現層爆炸的現象(簡單理解就是,很多不需要提升為合成層的元素因為某些不當操作成為了合成層)。解決層爆炸的問題,最佳方案是打破 overlap 的條件,也就是說讓其他元素不要和合成層元素重疊。簡單直接的方式:`使用3D硬件加速提升動畫性能時,最好給元素增加一個z-index屬性,人為干擾合成的排序,可以有效減少chrome創建不必要的合成層,提升渲染性能,移動端優化效果尤為明顯。
# 在chrome查看渲染層
其實在chrome中,也為我們提供了相關插件供我們查看頁面渲染層的分布情況以及GPU的占用率:
* chrome開發者工具菜單→more tools→Layers(開啟渲染層功能模塊)
* chrome開發者工具菜單→more tools→rendering(開啟渲染性能監測工具)
執行上面的操作后,你會在瀏覽器里看到這樣的效果:

* 最先是頁面右上方的小黑窗:其實提示已經說的很清楚了,它顯示的就是我們的GPU占用率,能夠讓我們清楚地知道頁面是否發生了大量的重繪。
* Layers版塊:這就是用于顯示我們剛提到的DOM渲染層的工具了,左側的列表里將會列出頁面里存在哪些渲染層,還有這些渲染層的詳細信息。
* Rendering版塊:這個版塊和我們的控制臺在同一個地方,大家可別找不到它。
前三個勾選項是我們最常使用的
* Paint flashing:勾選之后會對頁面中發生重繪的元素高亮顯示
* Layer borders:和我們的Layer版塊功能類似,它會用高亮邊界突出我們頁面中的各個渲染層
* FPS meter:就是開啟我們在(一)中提到的小黑窗,用于觀察我們的GPU占用率
# 參考資料
[瀏覽器渲染流程&Composite(渲染層合并)簡單總結](https://segmentfault.com/a/1190000014520786)
[前端性能優化之 Composite](https://segmentfault.com/a/1190000014520786)
- 第一部分 HTML
- meta
- meta標簽
- HTML5
- 2.1 語義
- 2.2 通信
- 2.3 離線&存儲
- 2.4 多媒體
- 2.5 3D,圖像&效果
- 2.6 性能&集成
- 2.7 設備訪問
- SEO
- Canvas
- 壓縮圖片
- 制作圓角矩形
- 全局屬性
- 第二部分 CSS
- CSS原理
- 層疊上下文(stacking context)
- 外邊距合并
- 塊狀格式化上下文(BFC)
- 盒模型
- important
- 樣式繼承
- 層疊
- 屬性值處理流程
- 分辨率
- 視口
- CSS API
- grid(未完成)
- flex
- 選擇器
- 3D
- Matrix
- AT規則
- line-height 和 vertical-align
- CSS技術
- 居中
- 響應式布局
- 兼容性
- 移動端適配方案
- CSS應用
- CSS Modules(未完成)
- 分層
- 面向對象CSS(未完成)
- 布局
- 三列布局
- 單列等寬,其他多列自適應均勻
- 多列等高
- 圣杯布局
- 雙飛翼布局
- 瀑布流
- 1px問題
- 適配iPhoneX
- 橫屏適配
- 圖片模糊問題
- stylelint
- 第三部分 JavaScript
- JavaScript原理
- 內存空間
- 作用域
- 執行上下文棧
- 變量對象
- 作用域鏈
- this
- 類型轉換
- 閉包(未完成)
- 原型、面向對象
- class和extend
- 繼承
- new
- DOM
- Event Loop
- 垃圾回收機制
- 內存泄漏
- 數值存儲
- 連等賦值
- 基本類型
- 堆棧溢出
- JavaScriptAPI
- document.referrer
- Promise(未完成)
- Object.create
- 遍歷對象屬性
- 寬度、高度
- performance
- 位運算
- tostring( ) 與 valueOf( )方法
- JavaScript技術
- 錯誤
- 異常處理
- 存儲
- Cookie與Session
- ES6(未完成)
- Babel轉碼
- let和const命令
- 變量的解構賦值
- 字符串的擴展
- 正則的擴展
- 數值的擴展
- 數組的擴展
- 函數的擴展
- 對象的擴展
- Symbol
- Set 和 Map 數據結構
- proxy
- Reflect
- module
- AJAX
- ES5
- 嚴格模式
- JSON
- 數組方法
- 對象方法
- 函數方法
- 服務端推送(未完成)
- JavaScript應用
- 復雜判斷
- 3D 全景圖
- 重載
- 上傳(未完成)
- 上傳方式
- 文件格式
- 渲染大量數據
- 圖片裁剪
- 斐波那契數列
- 編碼
- 數組去重
- 淺拷貝、深拷貝
- instanceof
- 模擬 new
- 防抖
- 節流
- 數組扁平化
- sleep函數
- 模擬bind
- 柯里化
- 零碎知識點
- 第四部分 進階
- 計算機原理
- 數據結構(未完成)
- 算法(未完成)
- 排序算法
- 冒泡排序
- 選擇排序
- 插入排序
- 快速排序
- 搜索算法
- 動態規劃
- 二叉樹
- 瀏覽器
- 瀏覽器結構
- 瀏覽器工作原理
- HTML解析
- CSS解析
- 渲染樹構建
- 布局(Layout)
- 渲染
- 瀏覽器輸入 URL 后發生了什么
- 跨域
- 緩存機制
- reflow(回流)和repaint(重繪)
- 渲染層合并
- 編譯(未完成)
- Babel
- 設計模式(未完成)
- 函數式編程(未完成)
- 正則表達式(未完成)
- 性能
- 性能分析
- 性能指標
- 首屏加載
- 優化
- 瀏覽器層面
- HTTP層面
- 代碼層面
- 構建層面
- 移動端首屏優化
- 服務器層面
- bigpipe
- 構建工具
- Gulp
- webpack
- Webpack概念
- Webpack工具
- Webpack優化
- Webpack原理
- 實現loader
- 實現plugin
- tapable
- Webpack打包后代碼
- rollup.js
- parcel
- 模塊化
- ESM
- 安全
- XSS
- CSRF
- 點擊劫持
- 中間人攻擊
- 密碼存儲
- 測試(未完成)
- 單元測試
- E2E測試
- 框架測試
- 樣式回歸測試
- 異步測試
- 自動化測試
- PWA
- PWA官網
- web app manifest
- service worker
- app install banners
- 調試PWA
- PWA教程
- 框架
- MVVM原理
- Vue
- Vue 餓了么整理
- 樣式
- 技巧
- Vue音樂播放器
- Vue源碼
- Virtual Dom
- computed原理
- 數組綁定原理
- 雙向綁定
- nextTick
- keep-alive
- 導航守衛
- 組件通信
- React
- Diff 算法
- Fiber 原理
- batchUpdate
- React 生命周期
- Redux
- 動畫(未完成)
- 異常監控、收集(未完成)
- 數據采集
- Sentry
- 貝塞爾曲線
- 視頻
- 服務端渲染
- 服務端渲染的利與弊
- Vue SSR
- React SSR
- 客戶端
- 離線包
- 第五部分 網絡
- 五層協議
- TCP
- UDP
- HTTP
- 方法
- 首部
- 狀態碼
- 持久連接
- TLS
- content-type
- Redirect
- CSP
- 請求流程
- HTTP/2 及 HTTP/3
- CDN
- DNS
- HTTPDNS
- 第六部分 服務端
- Linux
- Linux命令
- 權限
- XAMPP
- Node.js
- 安裝
- Node模塊化
- 設置環境變量
- Node的event loop
- 進程
- 全局對象
- 異步IO與事件驅動
- 文件系統
- Node錯誤處理
- koa
- koa-compose
- koa-router
- Nginx
- Nginx配置文件
- 代理服務
- 負載均衡
- 獲取用戶IP
- 解決跨域
- 適配PC與移動環境
- 簡單的訪問限制
- 頁面內容修改
- 圖片處理
- 合并請求
- PM2
- MongoDB
- MySQL
- 常用MySql命令
- 自動化(未完成)
- docker
- 創建CLI
- 持續集成
- 持續交付
- 持續部署
- Jenkins
- 部署與發布
- 遠程登錄服務器
- 增強服務器安全等級
- 搭建 Nodejs 生產環境
- 配置 Nginx 實現反向代理
- 管理域名解析
- 配置 PM2 一鍵部署
- 發布上線
- 部署HTTPS
- Node 應用
- 爬蟲(未完成)
- 例子
- 反爬蟲
- 中間件
- body-parser
- connect-redis
- cookie-parser
- cors
- csurf
- express-session
- helmet
- ioredis
- log4js(未完成)
- uuid
- errorhandler
- nodeclub源碼
- app.js
- config.js
- 消息隊列
- RPC
- 性能優化
- 第七部分 總結
- Web服務器
- 目錄結構
- 依賴
- 功能
- 代碼片段
- 整理
- 知識清單、博客
- 項目、組件、庫
- Node代碼
- 面試必考
- 91算法
- 第八部分 工作代碼總結
- 樣式代碼
- 框架代碼
- 組件代碼
- 功能代碼
- 通用代碼