我們從瀏覽器渲染一個頁面的行為說起:
從服務器端獲取的HTML文檔中構建出DOM樹(文檔對象模型);
樣式被加載和分析,構建CSSOM(CSS對象模型);
以DOM和CSSOM為基礎,文檔樹被構建,一系列對象被渲染(Webkit稱每一個為‘renderer’或’render 對象(render object)‘,Gecko中稱為‘frame’).渲染樹反應了除不可見元素(
, display:none)之外的DOM結構中一切可見元素,每一段字符串在渲染樹中都被當做獨立的渲染對象,每一個渲染對象都是其對應的DOM結構和計算所得樣式的混合體,換句話說渲染樹是DOM樹的視覺表現;對于每一個渲染樹元素,它的坐標都是通過計算獲得的,這被稱作排版(布局layout),瀏覽器通過文檔流的方式(也就是一次就能完成所有元素的布局)(tables需要多于一次的布局);
最終渲染樹出現在瀏覽器窗口上,這個過程稱為繪制(painting)。
當用戶與一個頁面交互或者腳本修改時,由于文檔結構的變化,以上的一些操作步驟會重新執行。
當元素樣式變化并不影響該元素在一個網頁上的位置時( background-color, border-color, visibility),瀏覽器只會把新樣式應用到該元素。
當改變影響了 文檔內容或結構或者元素的位置時,reflow發生(重新布局),這一般由以下因素觸發:
DOM操作(添加,刪除,更改,或者變更元素順序);
內容改變(包括表格區域內文本的變化)(所占位置大小變了);
計算或者改變CSS屬性(位置改變);
添加或者刪除樣式表;
改變類屬性(class)(可能會改變位置);
瀏覽器窗口操作(改變大小,滾動);
偽類激活(可能改變位置)
瀏覽器會盡可能的限制被改變元素所在區域的重排重繪,比如說 display:fixed/absolute元素改變時只會影響它本身和它的子元素,而 display:static元素改變時會使其隨后的元素都被重繪;(影響盡量少的元素)
另一個最大化性能的機制在于,當運行一系列JavaScript片段時,瀏覽器會緩存它們,然后一次運行。看下面的例子可以很好的理解:
var $body = $('body');$body.css('padding', '1px'); // reflow, repaint$body.css('color', 'red'); // repaint$body.css('margin', '2px'); // reflow, repaint// only 1 reflow and repaint will actually happen(由于緩存,只會重繪一次)
然而,就像上面已經提到的,調用一個元素的屬性會觸發強制性的reflow,當我們加上一行讀元素屬性的代碼時就會發生;
var $body = $('body');$body.css('padding', '1px');$body.css('padding'); // reading a property, a forced reflow(強制發生)$body.css('color', 'red');$body.css('margin', '2px');//另外一次reflow
因此,會有兩次reflow,因此應該組合來讀元素屬性已最大化性能 一個詳細的例子;
$(function() { var $body = $('body'); $body .on('click', '.block-1', function(e) { // 1 reflow $body.css('padding', '1px'); $body.css('color', 'red'); $body.css('margin', '2px'); }) .on('click', '.block-2', function(e) { // 2 reflows $body.css('padding', '1px'); $body.css('padding'); $body.css('color', 'red'); $body.css('margin', '2px'); }) .on('click', '.block-3', function(e) { // 3 repaints $body.css('color', 'red'); $body.css('color'); $body.css('color', 'yellow'); $body.css('background'); $body.css('color', 'blue'); $body.css('outline'); }) .on('click', '.block-4', function(e) { // 1 repaint $body.css('color', 'red'); $body.css('color', 'yellow'); $body.css('color', 'blue'); $body.css('color'); $body.css('background'); $body.css('outline'); }) .on('click', '.block-5', function(e) { // 3 reflows $body.css('padding', '1px'); $body[0].offsetHeight; $body.css('padding', '2px'); $body[0].offsetTop; $body.css('padding', '3px'); $body[0].offsetWidth; }) .on('click', '.block-6', function(e) { // 1 reflow $body.css('padding', '1px'); $body.css('padding', '2px'); $body.css('padding', '3px'); $body[0].offsetHeight; $body[0].offsetTop; $body[0].offsetWidth; });});
有些時候,可能你會需要一次強制性的reflow,例如:我們需要運用兩次 margin-left到同一個對象,第一次無動畫的設置到100px,然后通過動畫過渡到50px, 實例;
過渡動畫:
.has-transition { -webkit-transition: margin-left 1s ease-out; -moz-transition: margin-left 1s ease-out; -o-transition: margin-left 1s ease-out; transition: margin-left 1s ease-out;}
// our element that has a "has-transition" class by defaultvar $targetElem = $('#targetElemId');// remove the transition class$targetElem.removeClass('has-transition');// change the property expecting the transition to be off, as the class is not there// anymore$targetElem.css('margin-left', 100);// put the transition class back$targetElem.addClass('has-transition');// change the property$targetElem.css('margin-left', 50);
上述代碼并不按預期工作,因為改變被緩存并在最后執行了一次,這時候我們就需要一次強制性的執行了:
// remove the transition class$(this).removeClass('has-transition');// change the property$(this).css('margin-left', 100);// trigger a forced reflow, so that changes in a class/property get applied immediately$(this)[0].offsetHeight; // an example, other properties would work, too// put the transition class back$(this).addClass('has-transition');// change the property$(this).css('margin-left', 50);
現在達到預期效果了!
總結了一些有用的信息,本文有以下建議
構建有效的HTML和CSS,不要忘記聲明文檔編碼方式,樣式表應該包含在
標簽內,腳本文件應該放在 標簽的底部;簡化并且充分利用CSS選擇器(這一條被大多數使用CSS預處理器的開發者忽略),維護最少的層狀結構,以下是各選擇器的效率排行
#id.classdiva+iul>li*input[type='text']a:hover
應該引起注意的是,瀏覽器是從右向左讀取選擇器的,所以最右邊的應該選擇效率比較高的選擇器 #id,.class;
div * {...} // bad.list li {...} // bad.list-item {...} // good#list .list-item {...} // good
在腳本中,應該盡可能的減少DOM操作,如果對象和屬性會被重用,就緩存它們。在最好離線元素(未被插入文檔樹)(offline)上進行操作,然后把它插入DOM結構中;
如果使用jQuery,遵循[jQuery選擇器基本原則( http://learn.jquery.com/performance/optimize-selectors/);
修改元素的樣式時,修改class屬性是做好的方法,其位置越深,越好(also because this helps decouple logic from presentation);
只使 display:fixed/absolute的元素具有動畫;
不適用復雜的 :hover動畫也是一個好的實踐(給
添加 no-hover屬性), 延展閱讀;延展閱讀已獲得更多信息:
How browsers works;
Rendering: repaint, reflow/relayout, restyle;
原文鏈接
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com