1. 程式人生 > >瀏覽器渲染性能優化

瀏覽器渲染性能優化

個人 重要 繼續 like 外觀 ++ fig ger 研究

染性能

頁面不僅要快速加載,而且要順暢地運行;滾動應與手指的滑動一樣快,並且動畫和交互應如絲綢般順滑。

60fps 與設備刷新率

目前大多數設備的屏幕刷新率為 60 /。因此,如果在頁面中有一個動畫或漸變效果,或者用戶正在滾動頁面,那麽瀏覽器渲染動畫或頁面的每一幀的速率也需要跟設備屏幕的刷新率保持一致。

其中每個幀的預算時間僅比 16 毫秒多一點 (1 秒/ 60 = 16.66 毫秒)。但實際上,瀏覽器有整理工作要做,因此所有工作需要在 10 毫秒內完成。如果無法符合此預算,幀率將下降,並且內容會在屏幕上抖動(judders)。此現象通常稱為卡頓(jank,會對用戶體驗產生負面影響。

像素管道(The pixel pipeline)

在工作時需要了解並註意五個主要區域,這些是擁有最大控制權的部分,也是像素至屏幕管道中的關鍵點:

技術分享

  • JavaScript。一般來說,會使用 JavaScript 來實現一些視覺變化的效果。比如用 jQuery 的 animate 函數做一個動畫、對一個數據集進行排序或者往頁面裏添加一些 DOM 元素等。除了 JavaScript,還有其他一些常用方法也可以實現視覺變化效果,比如:CSS Animations、Transitions 和 Web Animation API。
  • 樣式計算Sytle calculations。This is the process of figuring out which CSS rules apply to which elements based on matching selectors, for example, .headline or .nav > .nav__item. From there, once rules are known, they are applied and the final styles for each element are calculated.
  • 布局。在知道對一個元素應用哪些規則之後,瀏覽器即可開始計算它要占據的空間大小及其在屏幕的位置。網頁的布局模式意味著一個元素可能影響其他元素,例如 <body> 元素的寬度一般會影響其子元素的寬度以及樹中各處的節點,因此對於瀏覽器來說,布局過程是經常發生的。
  • 繪制。繪制是填充像素的過程。它涉及繪出文本、顏色、圖像、邊框和陰影,包括元素的每個可視部分。繪制一般是在多個表面(通常稱為層layers)上完成的。
  • 合成。由於頁面的各部分可能被繪制到多層,由此它們需要按正確順序繪制到屏幕上,以便正確渲染頁面。對於與另一元素重疊的元素來說,這點特別重要,因為一個錯誤可能使一個元素錯誤地出現在另一個元素的上層。

管道的每個部分都有機會產生卡頓,因此務必準確了解代碼觸發管道的哪些部分。

不一定每幀都總是會經過管道每個部分的處理。實際上,不管是使用 JavaScript、CSS 還是網絡動畫,在實現視覺變化時,管道針對指定幀的運行通常有三種方式:

1. JS / CSS > 樣式 > 布局 > 繪制 > 合成

技術分享

如果修改元素的“layout”屬性,即改變了元素的幾何屬性(例如寬度、高度等),那麽瀏覽器將必須檢查所有其他元素,然後“自動重排”頁面(reflow the page)。任何受影響的部分都需要重新繪制,而且最終繪制的元素需進行合成。

2. JS / CSS > 樣式 > 繪制 > 合成

技術分享

如果修改“paint only”屬性(例如背景圖片、文字顏色或陰影等),即不會影響頁面布局的屬性,則瀏覽器會跳過布局,但仍將執行繪制。

3. JS / CSS > 樣式 > 合成

技術分享

如果更改一個既不用重新布局也不要重新繪制的屬性,則瀏覽器將只執行合成。這個最後的方式開銷最小,最適合於應用生命周期中的高壓力點,例如動畫或滾動。

性能是一種避免執行的藝術,並且使執行的任何操作盡可能高效。 許多情況下,這需要與瀏覽器配合,而不是跟它對著幹。 值得謹記的是,上面列出的各項管道工作在計算開銷上有所不同,一些任務比其他任務的開銷要大!

優化 JavaScript 執行

JavaScript often triggers visual changes. Sometimes that‘s directly through style manipulations, and sometimes it‘s calculations that result in visual changes, like searching or sorting data.時機不當或長時間運行的 JavaScript 可能是導致性能問題的常見原因,應當設法盡可能減少其影響。

JavaScript 性能分析可以說是一門藝術,因為編寫的 JavaScript 代碼與實際執行的代碼完全不像。現代瀏覽器使用 JIT 編譯器和各種各樣的優化和技巧來實現盡可能快的執行,這極大地改變了代碼的動態性。

一些幫助應用很好地執行 JavaScript的事情:

  • 對於動畫效果的實現,避免使用 setTimeout 或 setInterval,使用 requestAnimationFrame。
  • 將長時間運行的 JavaScript 從主線程移到 Web Worker。
  • 使用小任務來執行對多個幀的 DOM 更改。
  • 使用 Chrome DevTools 的 Timeline 和 JavaScript 分析器來評估 JavaScript 的影響。

使用 requestAnimationFrame 來實現視覺變化

當屏幕正在發生視覺變化時,最好在幀的開頭執行操作。保證 JavaScript 在幀開始時運行的唯一方式是使用 requestAnimationFrame。

/**
 * If run as a requestAnimationFrame callback, this
 * will be run at the start of the frame.
 */
function updateScreen(time) {
  // Make visual updates here.
}
requestAnimationFrame(updateScreen);

框架或示例可能使用 setTimeout 或 setInterval 來執行動畫之類的視覺變化,但這種做法的問題是,回調函數在幀中的某個時點運行,可能剛好在幀的末尾,而這經常會使我們丟失幀,導致卡頓。(composite等js的運行需要時間,會阻塞UI更新)。

技術分享

事實上,jQuery 目前的默認 animate 行為是使用 setTimeout!強烈建議打上補丁程序以使用 requestAnimationFrame。

降低復雜性或使用 Web Worker

JavaScript 在瀏覽器的主線程上運行,恰好與樣式計算、布局以及許多情況下的繪制一起運行。如果 JavaScript 運行時間過長,就會阻塞這些其他工作,可能導致幀丟失。

因此,要妥善處理 JavaScript 何時運行以及運行多久。例如,如果在滾動之類的動畫中,最好是想辦法使 JavaScript 保持在 3-4 毫秒的範圍內。超過此範圍,就可能要占用太多時間。如果在空閑期間,則可以不必那麽斤斤計較所占的時間。

在許多情況下,可以將純計算工作移到 Web Worker,例如,不需要 DOM 訪問權限,數據操作或遍歷(例如排序或搜索),往往很適合這種模型,加載和模型生成也是如此。

var dataSortWorker = new Worker("sort-worker.js");
dataSortWorker.postMesssage(dataToSort);
// The main thread is now free to continue working on other things...
dataSortWorker.addEventListener(‘message‘, function(evt) {
   var sortedData = evt.data;
   // Update data on screen...
});

並非所有工作都適合此模型:Web Worker 沒有 DOM 訪問權限。如果操作必須在主線程上執行,可以考慮一種批量方法,將大型任務分割為小任務,每個小任務所占時間不超過幾毫秒,並且在每幀的 requestAnimationFrame 處理程序內運行。

var taskList = breakBigTaskIntoMicroTasks(monsterTaskList);
requestAnimationFrame(processTaskList);
function processTaskList(taskStartTime) {
  var taskFinishTime;
  do {
    // Assume the next task is pushed onto a stack.
    var nextTask = taskList.pop();
    // Process nextTask.
    processTask(nextTask);
    // Go again if there’s enough time to do the next task.
    taskFinishTime = window.performance.now();
  } while (taskFinishTime - taskStartTime < 3);
  if (taskList.length > 0)
    requestAnimationFrame(processTaskList);
}

此方法會產生 UX 和 UI 後果,您將需要使用進度或活動指示器來確保用戶知道任務正在被處理。在任何情況下,此方法都不會占用應用的主線程,從而有助於主線程始終對用戶交互作出快速響應。

了解 JavaScript 的“frame tax”

在評估一個框架、庫或自己的代碼時,務必逐幀評估運行 JavaScript 代碼的開銷。當執行性能關鍵的動畫工作(例如變換或滾動)時,這點尤其重要。

測量 JavaScript 開銷和性能情況的最佳方法是使用 Chrome DevTools。通常,您將獲得如下的簡單記錄:

技術分享

The Main section provides a flame chart of JavaScript calls so you can analyze exactly which functions were called and how long each took.

如果發現有長時間運行的 JavaScript,則可以在 DevTools 用戶界面的頂部啟用 JavaScript 分析器:

技術分享


以這種方式分析 JavaScript 會產生開銷,因此一定只在想要更深入了解 JavaScript 運行時特性時才啟用它。啟用此復選框後,現在可以執行相同的操作,您將獲得有關 JavaScript 中調用了哪些函數的更多信息:

技術分享

有了這些信息之後,就可以評估 JavaScript 對應用性能的影響,並開始找出和修正函數運行時間過長的熱點(hotspots)。如前所述,應當設法移除長時間運行的 JavaScript,或者若不能移除,則將其移到 Web Worker 中,騰出主線程繼續執行其他任務。

避免微優化 JavaScript

知道瀏覽器執行一個函數版本比另一個函數要快 100 倍可能會很酷,比如請求元素的offsetTop比計算getBoundingClientRect()要快,但是,您在每幀調用這類函數的次數幾乎總是很少。因此,把重點放在 JavaScript 性能的這個方面通常是白費勁。您一般只能節省零點幾毫秒的時間。

如果您開發的是遊戲或計算開銷很大的應用,則可能屬於本指南的例外情況,因為您一般會將大量計算放入單個幀,在這種情況下各種方法都很有用。

簡而言之,慎用微優化,因為它們通常不會映射到您正在構建的應用類型。2/8法則,先從瓶頸處著手優化。

縮小樣式計算的範圍並降低其復雜性

通過添加和刪除元素,更改屬性、類或通過動畫來更改 DOM,都會導致瀏覽器重新計算元素樣式,在很多情況下還會對頁面或頁面的一部分進行布局(即自動重排)。This process is called computed style calculation.

計算樣式的第一部分是創建一組匹配選擇器,這實質上是瀏覽器計算出給指定元素應用哪些classes, pseudo-selectors and IDs。

第二部分涉及從匹配選擇器中獲取所有樣式規則,並計算出此元素的最終樣式。在 Blink(Chrome 和 Opera 的渲染引擎)中,這些過程的開銷至少在目前是大致相同的:

Roughly 50% of the time used to calculate the computed style for an element is used to match selectors,而另一半時間用於從匹配的規則中構建 RenderStyle(computed style representation)。

  • 降低選擇器的復雜性;使用以類為中心的方法,例如 BEM規範(Block-Element_Modifer)。
  • 減少必須計算其樣式的元素數量。

降低選擇器的復雜性

在最簡單的情況下,在 CSS 中只有一個類的元素:

.title {
/* styles */
}

但是,隨著項目的增長,將可能產生更復雜的 CSS,最終的選擇器可能變成這樣:

.box:nth-last-child(-n+1) .title {
/* styles */
}

為了知道是否需要應用樣式,瀏覽器實際上必須詢問“這是否為有 title 類的元素,其父元素恰好是負第 N 個子元素加上 1 個帶 box 類的元素?”計算此結果可能需要大量時間,具體取決於所用的選擇器和相應的瀏覽器。特定的選擇器可以更改為一個類:

.final-box-title {
/* styles */
}

開發者可能對該類的名稱有疑問,但此工作對於瀏覽器而言要簡單得多。在上一版本中,為了知道該元素是否為其類型的最後一個,瀏覽器首先必須知道關於其他元素的所有情況,以及其後面是否有任何元素會是第 N 個最後子元素,這比簡單地將類選擇器與元素匹配的開銷要大得多。

生成render tree時,對於每個DOM元素,必須在所有Style Rules中找到符合的 selector 並將對應的樣式規則進行合並。
css選擇器的解析是從右往左的,這樣公共樣式就在CSSOM樹的父節點上,更具體的樣式(選擇器更具體)會在子節點上,節點分支和遍歷次數都會變少。如果采用 left-to-right 的方式讀取css規則,那麽大多數規則讀到最後才會發現是不匹配,做了很多無用功;而采取 right-to-left 的方式,只要發現最右邊選擇器不匹配,就直接舍棄,避免很多無效匹配。

減少要計算樣式的元素數量

另一個性能考慮,在元素更改時需要計算的工作量對於許多樣式更新而言是更重要的因素。

In general terms, the worst case cost of calculating the computed style of elements is the number of elements multiplied by the selector count, because each element needs to be at least checked once against every style rule to see if it matches.

註:以前曾經是這樣:如果改變了(例如)body 元素上的一個類,則該頁的所有子元素將需要重新計算其計算樣式。現在有點不一樣:對於更改時會導致重新計算樣式的元素,某些瀏覽器維護一小組每個這種元素獨有的規則。這意味著,根據元素在樹中的位置以及所改變的具體屬性,元素不一定需要重新計算。

樣式計算可能經常是直接針對少量目標元素,而不是聲明整個頁面無效。在現代瀏覽器中,這往往不再是個問題,因為瀏覽器並不一定需要檢查一項更改可能影響的所有元素。另一方面,較早的瀏覽器不一定針對此類任務進行了優化。應當盡可能減少聲明為無效的元素的數量

註:如果您熱衷於網頁組件,有一點值得註意,樣式計算在這方面稍有不同,因為默認情況下樣式不會跨越 Shadow DOM 的邊界,並且範圍限於單個組件,而不是整個樹。但是,總體來看,同樣的概念仍然適用:規則簡單的小樹比規則復雜的大樹會得到更高效地處理。

測量樣式重新計算的開銷

測量樣式重新計算的最簡單、最好的方法是使用 Chrome DevTools 的 Timeline 模式。首先,打開 DevTools,轉至 Timeline 選項卡,選中記錄並與您的網站交互。停止記錄後,將看到下圖所示情況。

技術分享

頂部的條表示每秒幀數,如果看到柱形超過較低的線,即 60fps 線,則存在長時間運行的幀。

技術分享

如果一些滾動之類的交互或其他交互時出現長時間運行的幀,則應當進一步審查。

如果出現較大的紫色塊,如上例所示,請點擊記錄了解到更多細節。

技術分享

在這次抓取中,有一個長時間運行的重新計算樣式事件,其時間剛好超過 18 毫秒,並且恰好發生在滾動期間,導致用戶體驗到明顯的抖動。

如果點擊事件本身,將看到一個調用棧,精確指出了您的 JavaScript 中導致觸發樣式更改的位置。此外,還獲得樣式受更改影響的元素數量(本例中剛好超過 400 個元素),以及執行樣式計算所花的時間。您可以使用此信息來開始嘗試在代碼中查找修正點。

使用BEM規範

BEM的編碼方法實際上納入了上述選擇器匹配的性能優勢,因為它建議所有元素都有單個類,並且在需要層次結構時也納入了類的名稱:

.list { }
.list__list-item { }

如果需要一些修飾符,像在上面我們想為最後一個子元素做一些特別的東西,就可以按如下方式添加:

.list__list-item--last-child {}

如果您在尋找一種好方法來組織您的 CSS,則 BEM 真的是個很好的起點,不僅從結構的角度如此,還因為樣式查找得到了簡化。

避免大型、復雜的布局和布局抖動

布局是瀏覽器計算各元素幾何信息的過程:元素的大小以及在頁面中的位置。 根據所用的 CSS、元素的內容或父級元素,每個元素都將有顯式或隱含的大小信息。此過程在 Chrome、Opera、Safari 和 Internet Explorer 中稱為布局 (Layout)。 在 Firefox 中稱為自動重排 (Reflow),但實際上其過程是一樣的。

與樣式計算相似,布局開銷的直接考慮因素如下:

  1. 需要布局的元素數量。
  2. 這些布局的復雜性。
  • 布局的作用範圍一般為整個文檔。
  • DOM 元素的數量將影響性能,應盡可能避免觸發布局。
  • 評估布局模型的性能;新版 Flexbox比舊版 Flexbox 或基於浮動的布局模型更快。
  • Avoid forced synchronous layouts and layout thrashing; read style values then make style changes.

盡可能避免布局操作

當更改樣式時,瀏覽器會檢查更改是否需要計算布局,以及是否需要更新渲染樹。幾何屬性(如寬度、高度、左側或頂部)的更改都需要布局計算。

.box {
width: 20px;
height: 20px;
}
/** Changing width and height triggers layout. */
.box--expanded {
width: 200px;
height: 350px;
}

布局幾乎總是作用到整個文檔。 如果有大量元素,將需要很長時間來算出所有元素的位置和尺寸。

如果無法避免布局,關鍵還是要使用 Chrome DevTools 來查看布局要花多長時間,並確定布局是否是造成瓶頸的原因。首先,打開 DevTools,選擇“Timeline”標簽,點擊“record”按鈕,然後與您的網站交互。當您停止記錄時,將看到網站表現情況的詳細分析:

技術分享

在仔細研究上例中的框架時,我們看到超過 20 毫秒用在布局上,當我們在動畫中設置 16 毫秒來獲取屏幕上的幀時,此布局時間太長。您還可以看到,DevTools 將說明樹的大小(本例中為 1618 個元素)以及需要布局的節點數。

使用 flexbox 而不是較早的布局模型

網頁有各種布局模型,一些模式比其他模式受到更廣泛的支持。最早的 CSS 布局模型使我們能夠在屏幕上對元素進行相對、絕對定位或通過浮動元素定位。

下面的屏幕截圖顯示了在 1,300 個框上使用浮動的布局開銷。當然,這是一個人為的例子,因為大多數應用將使用各種手段來定位元素。

技術分享

如果我們更新此示例以使用 Flexbox(Web 平臺的新模型),則出現不同的情況:

技術分享

現在,對於相同數量的元素和相同的視覺外觀,布局的時間要少得多(本例中為分別 3.5 毫秒和 14 毫秒)。務必記住,對於某些情況,可能無法選擇 Flexbox,因為它沒有浮動那麽受支持,但是在可能的情況下,至少應研究布局模型對網站性能的影響,並且采用最大程度減少網頁執行開銷的模型。

在任何情況下,不管是否選擇 Flexbox,都應當在應用的高壓力點期間嘗試完全避免觸發布局

避免強制同步布局

將一幀送到屏幕會采用如下順序:

技術分享

首先 JavaScript 運行,然後計算樣式,然後布局。但是,JavaScript 在更改元素樣式後,獲取其幾何屬性的值,此時會強制瀏覽器應用新樣式提前執行布局,值後才能獲取幾何屬性值。這被稱為強制同步布局(forced synchronous layout

要記住的第一件事是,在 JavaScript 運行時,來自上一幀的所有舊布局值是已知的,並且可供您查詢。因此,如果(例如)您要在幀的開頭寫出一個元素(讓我們稱其為“框”)的高度,可能編寫一些如下代碼:

// Schedule our function to run at the start of the frame.
requestAnimationFrame(logBoxHeight);
function logBoxHeight() {
  // Gets the height of the box in pixels and logs it out.
  console.log(box.offsetHeight);
}

如果在請求此框的高度之前,已更改其樣式,就會出現問題:

function logBoxHeight() {
  box.classList.add(‘super-big‘);    //樣式更改後,瀏覽器必須先應用新的樣式(重繪)之後才能獲取當前的值,有時是多做無用功
  // Gets the height of the box in pixels and logs it out.
  console.log(box.offsetHeight);
}

現在,為了獲得框的高度,瀏覽器必須先應用樣式更改(由於增加了 super-big 類),然後運行布局,這時才能返回正確的高度。這是不必要的,並且可能開銷很大。

因此,始終應先批量讀取樣式並執行(瀏覽器可以使用上一幀的布局值),然後執行任何賦值操作。

以上函數應為:

function logBoxHeight() {
  // Gets the height of the box in pixels and logs it out.
  console.log(box.offsetHeight);
  box.classList.add(‘super-big‘);
}

大部分情況下,並不需要先應用新樣式然後查詢值,使用上一幀的值就足夠了。與瀏覽器同步(或比其提前)運行樣式計算和布局可能成為瓶頸。

避免布局超負荷(thrashing)

有一種方式會使強制同步布局更糟:連續執行大量這種強制布局。如下:

function resizeAllParagraphsToMatchBlockWidth() {
  // Puts the browser into a read-write-read-write cycle.
  for (var i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = box.offsetWidth + ‘px‘;
  }
}

此代碼循環處理一組段落,並設置每個段落的寬度以匹配一個稱為“box”的元素的寬度。這看起來沒有害處,但問題是循環的每次叠代讀取一個樣式值 (box.offsetWidth),然後立即使用此值來更新段落的寬度 (paragraphs[i].style.width)。在循環的下次叠代時,瀏覽器必須考慮樣式已更改這一事實,因為 offsetWidth 是上次請求的(在上一次叠代中),所以它必須應用更改的樣式,然後運行布局。每次叠代都將出現此問題!

此示例的修正方法還是先讀取值,然後寫入值:

// Read.
var width = box.offsetWidth;
function resizeAllParagraphsToMatchBlockWidth() {
  for (var i = 0; i < paragraphs.length; i++) {
    // Now write.
    paragraphs[i].style.width = width + ‘px‘;
  }
}

如果要保證安全,應當查看 FastDOM,它會自動批處理讀取和寫入,應當能防止意外觸發強制同步布局或布局抖動。

簡化繪制的復雜度、減小繪制區域

繪制是填充像素的過程,像素最終合成到用戶的屏幕上。 它往往是管道中運行時間最長的任務,應盡可能避免此任務。

  • 除 transform 或 opacity 屬性之外,更改任何屬性始終都會觸發繪制。
  • 繪制通常是像素管道中開銷最大的部分,應盡可能避免繪制。
  • 通過layer promotion和動畫的編排來減少繪制區域。
  • 使用 Chrome DevTools paint profile來評估繪制的復雜性和開銷;應盡可能降低復雜性並減少開銷。

觸發布局與繪制

如果觸發布局,則總是會觸發繪制,因為更改任何元素的幾何屬性意味著其像素需要修正!

技術分享

如果更改非幾何屬性,例如背景、文本或陰影,也可能觸發繪制。在這些情況下,不需要布局,並且管道將如下所示:

技術分享

使用 Chrome DevTools 快速確定繪制瓶頸

技術分享

您可以使用 Chrome DevTools 來快速確定正在繪制的區域。打開 DevTools,按下鍵盤上的 Esc 鍵。在出現的面板中,轉到“rendering”標簽,然後選中“Show paint rectangles”。

打開此選項後,每次發生繪制時,Chrome 將讓屏幕閃爍綠色。如果看到整個屏幕閃爍綠色,或看到不應繪制的屏幕區域,則應當進一步研究。

技術分享

Chrome DevTools Timeline 中有一個選項提供更多信息:繪制分析器。要啟用此選項,轉至 Timeline,然後選中頂部的“Paint”框。需要註意的是,請務必僅在嘗試分析繪制問題時才打開此選項,因為它會產生開銷,並且會影響性能分析結果。最好是在想要更深入了解具體繪制內容時使用。

技術分享

完成了上述設置之後,現在可以運行 Timeline 錄制,並且繪制記錄將包含更多的細節。通過點擊一幀的繪制記錄,您將進入該幀的繪制分析器:

技術分享

點擊繪制分析器將調出一個視圖,您可以查看所繪制的元素、所花的時間,以及所需的各個繪制調用:

此分析器顯示區域和復雜性(實際上就是繪制所花的時間),如果不能選擇避免繪制,這兩個都是可以設法修正的方面。

提升移動或淡出的元素

繪制並非總是繪制到內存中的單個圖像。事實上,在必要時瀏覽器可以繪制到多個圖像或合成層(compositor layers)。

技術分享

此方法的優點是,定期重繪的或通過變形在屏幕上移動的元素,可以在不影響其他元素的情況下進行處理。Sketch、GIMP 或 Photoshop 之類的藝術文件也是如此,各個層可以在彼此的上面處理並合成,以創建最終圖像。

創建新層的最佳方式是使用 will-change CSS 屬性。此方法在 Chrome、Opera 和 Firefox 上有效,並且通過 transform 的值將創建一個新的合成器層:

.moving-element {
will-change: transform;
}

對於不支持 will-change 但受益於層創建的瀏覽器,例如 Safari 和 Mobile Safari,需要使用3D 變形來強制創建一個新層:

.moving-element {
transform: translateZ(0);
}

但需要註意的是:不要創建太多層,因為每層都需要內存和管理開銷。

如果已將一個元素提升到一個新層,可使用 DevTools 確認這樣做已帶來性能優勢。請勿在不分析的情況下提升元素。

減少繪制區域

然而有時,雖然提升元素,卻仍需要繪制工作。繪制問題的一個大挑戰是,瀏覽器將兩個需要繪制的區域聯合在一起,而這可能導致整個屏幕重繪。因此,如果頁面頂層有一個固定標頭,而在屏幕底部還有正在繪制的元素,則整個屏幕可能最終要重繪。

減少繪制區域往往是編排動畫和變換,使其不過多重疊,或設法避免對頁面的某些部分設置動畫。

降低繪制的復雜性

在談到繪制時,一些繪制比其他繪制的開銷更大。例如,繪制任何涉及模糊(例如陰影)的元素所花的時間將比(例如)繪制一個紅框的時間要長。但是,對於 CSS 而言,這點並不總是很明顯:background: red; 和 box-shadow: 0, 4px, 4px, rgba(0,0,0,0.5); 看起來不一定有截然不同的性能特性,但確實很不相同。

利用上述繪制分析器,您可以確定是否需要尋求其他方式來實現效果。問問自己,是否可能使用一組開銷更小的樣式或替代方式來實現最終結果。

您要盡可能的避免繪制的發生,特別是在動畫效果中。因為每幀 10 毫秒的時間預算一般來說是不足以完成繪制工作的,尤其是在移動設備上。

瀏覽器渲染性能優化