首頁 web前端 js教程 高效能JavaScript 重排與重繪(2)_javascript技巧

高效能JavaScript 重排與重繪(2)_javascript技巧

May 16, 2016 pm 03:45 PM

先回顧下前一篇文章高性能JavaScript DOM編程,主要提了兩點優化,一是盡量減少DOM的訪問,而把運算放在ECMAScript這一端,二是盡量緩存局部變量,例如length等等,最後介紹了兩個新的API querySelector()以及querySelectorAll(),在做組合選擇的時候可以大膽使用。而本文主要講的是DOM程式設計可能最耗時的地方,重排與重繪

1、什麼是重排、重繪
瀏覽器下載完頁面中的所有元件-HTML標記、JavaScript、CSS、圖片之後會解析產生兩個內部資料結構-DOM樹和渲染樹。

DOM樹表示頁面結構,渲染樹表示DOM節點如何顯示。 DOM樹中的每一個需要顯示的節點在渲染樹種至少存在一個對應的節點(隱藏的DOM元素disply值為none 在渲染樹中沒有對應的節點)。渲染樹中的節點被稱為「幀」或「盒",符合CSS模型的定義,理解頁面元素為一個具有填充,邊距,邊框和位置的盒子。一旦DOM和渲染樹建置完成,瀏覽器就會開始顯示(繪製)頁面元素。

當DOM的變化影響了元素的幾何屬性(寬或高),瀏覽器需要重新計算元素的幾何屬性,同樣其他元素的幾何屬性和位置也會因此受到影響。瀏覽器會使渲染樹中受到影響的部分失效,並重新建構渲染樹。這個過程稱為重排。完成重排後,瀏覽器會重新繪製受影響的部分到螢幕,該過程稱為重繪。由於瀏覽器的流佈局,對渲染樹的計算通常只需要遍歷一次就可以完成。但table及其內部元素除外,它可能需要多次計算才能確定好其在渲染樹中節點的屬性,通常要花3倍於同等元素的時間。這也是為什麼我們要避免用table來做版面的一個原因。

並不是所有的DOM變化都會影響幾何屬性,例如改變一個元素的背景色並不會影響元素的寬和高,這種情況只會發生重繪。

2、重排和重繪的代價究竟多大
重排和重繪的代價有多大?我們再回到前文那個過橋的例子上,細心的你可能會發現了,千倍的時間差並不是由於「過橋」一手造成的,每次「過橋」其實都伴隨著重排和重繪,而耗能的絕大部分也正是在這裡!

var times = 15000;

// code1 每次过桥+重排+重绘
console.time(1);
for(var i = 0; i < times; i++) {
 document.getElementById('myDiv1').innerHTML += 'a';
}
console.timeEnd(1);

// code2 只过桥
console.time(2);
var str = '';
for(var i = 0; i < times; i++) {
 var tmp = document.getElementById('myDiv2').innerHTML;
 str += 'a';
}
document.getElementById('myDiv2').innerHTML = str;
console.timeEnd(2);

// code3 
console.time(3);
var _str = '';
for(var i = 0; i < times; i++) {
 _str += 'a';
}
document.getElementById('myDiv3').innerHTML = _str;
console.timeEnd(3);


// 1: 2874.619ms
// 2: 11.154ms
// 3: 1.282ms
登入後複製

資料是不會說謊的,看到了吧,多次訪問DOM對於重排和重繪來說,耗時簡直不值一提了。

3、重排何時發生
很顯然,每次重排,必然會導致重繪,那麼,重排會在哪些情況下發生?

1、新增或刪除可見的DOM元素
2、元素位置改變
3.元素尺寸改變
4.元素內容改變(例如:一個文字被另一個不同尺寸的圖片取代)
5.頁面渲染初始化(這個無法避免)
6.瀏覽器視窗尺寸改變
這些都是顯而易見的,或許你已經有過這樣的體會,不間斷地改變瀏覽器視窗大小,導致UI反應遲鈍(某些低版本IE下甚至直接掛掉),現在你可能恍然大悟,沒錯,正是一次次的重排重繪導致的!

4、渲染樹變化的排隊與刷新
思考下面程式碼:

var ele = document.getElementById('myDiv');
ele.style.borderLeft = '1px';
ele.style.borderRight = '2px';
ele.style.padding = '5px';
登入後複製

乍一想,元素的樣式改變了三次,每次改變都會引起重排和重繪,所以總共有三次重排重繪過程,但是瀏覽器並不會這麼笨,它會把三次修改“保存」起來(大多數瀏覽器透過隊列化修改並批量執行來優化重排過程),一次完成!但是,有些時候你可能會(經常是不知不覺)強制刷新隊列並要求計劃任務立即執行。取得佈局資訊的操作會導致佇列刷新,例如:

1.offsetTop, offsetLeft, offsetWidth, offsetHeight
2.scrollTop, scrollLeft, scrollWidth, scrollHeight
3.clientTop, clientLeft, clientWidth, clientHeight
4.getComputedStyle() (currentStyle in IE)
將上面的程式碼稍加修改:

var ele = document.getElementById('myDiv');
ele.style.borderLeft = '1px';
ele.style.borderRight = '2px';

// here use offsetHeight
// ...
ele.style.padding = '5px';

登入後複製

因为offsetHeight属性需要返回最新的布局信息,因此浏览器不得不执行渲染队列中的“待处理变化”并触发重排以返回正确的值(即使队列中改变的样式属性和想要获取的属性值并没有什么关系),所以上面的代码,前两次的操作会缓存在渲染队列中待处理,但是一旦offsetHeight属性被请求了,队列就会立即执行,所以总共有两次重排与重绘。所以尽量不要在布局信息改变时做查询。

5、最小化重排和重绘
我们还是看上面的这段代码:

var ele = document.getElementById('myDiv');
ele.style.borderLeft = '1px';
ele.style.borderRight = '2px';
ele.style.padding = '5px';
登入後複製

三个样式属性被改变,每一个都会影响元素的几何结构,虽然大部分现代浏览器都做了优化,只会引起一次重排,但是像上文一样,如果一个及时的属性被请求,那么就会强制刷新队列,而且这段代码四次访问DOM,一个很显然的优化策略就是把它们的操作合成一次,这样只会修改DOM一次:

var ele = document.getElementById('myDiv');

// 1. 重写style
ele.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';

// 2. add style
ele.style.cssText += 'border-;eft: 1px;'

// 3. use class
ele.className = 'active';
登入後複製

6、fragment元素的应用
看如下代码,考虑一个问题:

<ul id='fruit'>
 <li> apple </li>
 <li> orange </li>
</ul>
登入後複製

如果代码中要添加内容为peach、watermelon两个选项,你会怎么做?

var lis = document.getElementById('fruit');
var li = document.createElement('li');
li.innerHTML = 'apple';
lis.appendChild(li);

var li = document.createElement('li');
li.innerHTML = 'watermelon';
lis.appendChild(li);

登入後複製

很容易想到如上代码,但是很显然,重排了两次,怎么破?前面我们说了,隐藏的元素不在渲染树中,太棒了,我们可以先把id为fruit的ul元素隐藏(display=none),然后添加li元素,最后再显示,但是实际操作中可能会出现闪动,原因这也很容易理解。这时,fragment元素就有了用武之地了。

var fragment = document.createDocumentFragment();

var li = document.createElement('li');
li.innerHTML = 'apple';
fragment.appendChild(li);

var li = document.createElement('li');
li.innerHTML = 'watermelon';
fragment.appendChild(li);

document.getElementById('fruit').appendChild(fragment);

登入後複製

文档片段是个轻量级的document对象,它的设计初衷就是为了完成这类任务——更新和移动节点。文档片段的一个便利的语法特性是当你附加一个片断到节点时,实际上被添加的是该片断的子节点,而不是片断本身。只触发了一次重排,而且只访问了一次实时的DOM。

7、让元素脱离动画流
用展开/折叠的方式来显示和隐藏部分页面是一种常见的交互模式。它通常包括展开区域的几何动画,并将页面其他部分推向下方。

一般来说,重排只影响渲染树中的一小部分,但也可能影响很大的部分,甚至整个渲染树。浏览器所需要重排的次数越少,应用程序的响应速度就越快。因此当页面顶部的一个动画推移页面整个余下的部分时,会导致一次代价昂贵的大规模重排,让用户感到页面一顿一顿的。渲染树中需要重新计算的节点越多,情况就会越糟。

使用以下步骤可以避免页面中的大部分重排:

使用绝对位置定位页面上的动画元素,将其脱离文档流
让元素动起来。当它扩大时,会临时覆盖部分页面。但这只是页面一个小区域的重绘过程,不会产生重排并重绘页面的大部分内容。
当动画结束时恢复定位,从而只会下移一次文档的其他元素
总结
重排和重绘是DOM编程中耗能的主要原因之一,平时涉及DOM编程时可以参考以下几点:

尽量不要在布局信息改变时做查询(会导致渲染队列强制刷新)
同一个DOM的多个属性改变可以写在一起(减少DOM访问,同时把强制渲染队列刷新的风险降为0)
如果要批量添加DOM,可以先让元素脱离文档流,操作完后再带入文档流,这样只会触发一次重排(fragment元素的应用)
将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位。

以上就是高性能JavaScript 重排与重绘的全部介绍内容,大家可以结合上一篇高性能JavaScript DOM编程(1)一起学习,

希望这两篇文章可以帮到大家,解决大家这方面的疑惑。

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

前端熱敏紙小票打印遇到亂碼問題怎麼辦? 前端熱敏紙小票打印遇到亂碼問題怎麼辦? Apr 04, 2025 pm 02:42 PM

前端熱敏紙小票打印的常見問題與解決方案在前端開發中,小票打印是一個常見的需求。然而,很多開發者在實...

神秘的JavaScript:它的作用以及為什麼重要 神秘的JavaScript:它的作用以及為什麼重要 Apr 09, 2025 am 12:07 AM

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

誰得到更多的Python或JavaScript? 誰得到更多的Python或JavaScript? Apr 04, 2025 am 12:09 AM

Python和JavaScript開發者的薪資沒有絕對的高低,具體取決於技能和行業需求。 1.Python在數據科學和機器學習領域可能薪資更高。 2.JavaScript在前端和全棧開發中需求大,薪資也可觀。 3.影響因素包括經驗、地理位置、公司規模和特定技能。

如何使用JavaScript將具有相同ID的數組元素合併到一個對像中? 如何使用JavaScript將具有相同ID的數組元素合併到一個對像中? Apr 04, 2025 pm 05:09 PM

如何在JavaScript中將具有相同ID的數組元素合併到一個對像中?在處理數據時,我們常常會遇到需要將具有相同ID�...

JavaScript難以學習嗎? JavaScript難以學習嗎? Apr 03, 2025 am 12:20 AM

學習JavaScript不難,但有挑戰。 1)理解基礎概念如變量、數據類型、函數等。 2)掌握異步編程,通過事件循環實現。 3)使用DOM操作和Promise處理異步請求。 4)避免常見錯誤,使用調試技巧。 5)優化性能,遵循最佳實踐。

如何實現視差滾動和元素動畫效果,像資生堂官網那樣?
或者:
怎樣才能像資生堂官網一樣,實現頁面滾動伴隨的動畫效果? 如何實現視差滾動和元素動畫效果,像資生堂官網那樣? 或者: 怎樣才能像資生堂官網一樣,實現頁面滾動伴隨的動畫效果? Apr 04, 2025 pm 05:36 PM

實現視差滾動和元素動畫效果的探討本文將探討如何實現類似資生堂官網(https://www.shiseido.co.jp/sb/wonderland/)中�...

console.log輸出結果差異:兩次調用為何不同? console.log輸出結果差異:兩次調用為何不同? Apr 04, 2025 pm 05:12 PM

深入探討console.log輸出差異的根源本文將分析一段代碼中console.log函數輸出結果的差異,並解釋其背後的原因。 �...

JavaScript的演變:當前的趨勢和未來前景 JavaScript的演變:當前的趨勢和未來前景 Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

See all articles