標籤:style http java color 使用 strong
Alexander Skutin , 2014.5.26 . 由Max shirshin與2014年6月30日翻譯(俄語 -> 英語) 現今我們應更加註重網頁渲染,及其在web開發中的重要性。雖然很多文章都曾談到這一主題,但大多是分散和割裂的。譬如為了對這個主題有更全面的認識需要去搜尋很多的資訊來源,而這也是筆者決定寫這篇文章的原因。筆者相信本篇文章會有益於初級開發人員,當然對希望能夠更新和整理已有知識的中進階開發人員同樣能夠有所裨益。 當頁面配置定義完成後,頁面渲染的過程與樣式和指令碼所承擔的重要角色一樣,也需要在初期就開始進行最佳化。專業的開發人員需要瞭解這些技巧以避免效能的問題。 本篇文章並不會詳細介紹瀏覽器的內部工作機制,但將會提供一些通用的規則。這是由於不同的瀏覽器引擎有著不同的工作方式,太針對與某一個特定瀏覽器的研究會使這個過程變得過於複雜。 瀏覽器如何渲染網頁? 首先概述瀏覽器渲染網頁的過程: 1、文件物件模型(Document Object Model = DOM)將首先從來自於伺服器的HTML中產生。 2、載入並解析樣式,形成樣式物件模型(CSS Object Mode = CSSOM)。 3、在DOM和CSSOM上層,建立一個渲染樹,這是由將被渲染的對象形成的集合(webkit核心稱這些對象為“渲染者”或“渲染對象”,在Gecko核心中則稱為“幀”)。渲染樹會影響DOM結構,但隱藏元素則不在此列(例如<head>標籤,或具有display:none屬性的元素)。在渲染樹中的每個文本字串都被表述為一個單獨的渲染對象,而每個渲染對象都將包含其應有的DOM結構(或文字區塊)以及計算後的樣式。從另一個角度上說,這個渲染樹描述了DOM中的可見表徵。 4、計算渲染樹中元素的座標,從而成為“層”。瀏覽器使用一系列方法(這一系列方法僅需要一個節拍(pass))來放置所有的元素(表格需要多個節拍)。 5、最後,頁面將在瀏覽器視窗中顯示出來,這個過程稱為“繪製”。 當使用者與頁面互動時,或者指令碼改變頁面時,前面提及的這些操作都會重複執行,頁面配置改變時同樣如此。 重繪 當改變了元素樣式,而這個變動並不會影響元素在頁面中的位置時(例如background-color,border-color,visibility),瀏覽器會依據新的樣式僅重繪這個元素(即重繪和重新應用樣式會發生) 重新布局 當改變影響了文檔內容、結構、或元素位置時,布局會重新應用和產生。這些改變通常由以下行為觸發:
- DOM操作(元素的添加、刪除、修改以及調整順序)
- 內容變更,包括表單域中的文字修改
- CSS屬性的計算和修改
- 樣式表的添加和移除
- class屬性的修改
- 瀏覽器視窗變動(大小、滾動)
- 偽類啟用(:hover)
如何最佳化瀏覽器渲染 瀏覽器會儘可能的將重繪和重排限制在受影響的元素地區內。例如,對一個絕對位置/懸浮定位的元素尺寸大小的變更會僅僅影響這個元素及他的後代,然而一個靜態定位元素的尺寸變化會讓在其之後的所有元素都觸發重新布局。 另一個最佳化的技巧是,當運行javascript代碼時,瀏覽器會緩衝這些變更,然後在代碼運行之後,將變更放到一個單獨的節拍中再應用。例如,下面的代碼會僅觸發一次重繪和重新布局。
var $body = $(‘body‘);
$body.css(‘padding‘, ‘1px‘); // 重排, 重繪
$body.css(‘color‘, ‘red‘); // 重繪
$body.css(‘margin‘, ‘2px‘); // 重排,重繪
// 以上步驟下實際僅發生一次重排和重繪 但是就像上面提到的,重新計算一個元素屬性將會觸發一次強制的重新布局。當我們添加額外一行代碼來擷取元素屬性時,將會產生一次強制重排
var $body = $(‘body‘);
$body.css(‘padding‘, ‘1px‘);
$body.css(‘padding‘); //擷取屬性,強制重排 $body.css(‘color‘, ‘red‘);
$body.css(‘margin‘, ‘2px‘);
最終,我們將造成兩次重排而並非只是一次。正因如此,你需要將擷取元素屬性歸到一次,來最佳化效能。(來看樣本) 當你不得不觸發強制重繪時,會有一些狀況發生。例如:我們將要把一條相同的屬性兩次應用到一個相同的元素上。最初,這個元素會以無動畫的方式設為100px,然後需要帶動畫的調整為50px。你可以通過這個樣本研究,但我會更詳細的描述這個情況。 我們首先建立一個具有transition屬性的css類
.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;
}
然後進行以下處理: // 具有has-transition類的元素
var $targetElem = $(‘#targetElemId‘);
//移除has-transition類
$targetElem.removeClass(‘has-transition‘);
// 在css類不再存在後,改變這個屬性確認動畫已經去除
$targetElem.css(‘margin-left‘, 100);
// 增加has-transition類
$targetElem.addClass(‘has-transition‘);
// 修改屬性
$targetElem.css(‘margin-left‘, 50);
這個實現並不會像期望的方式工作。這些改變被緩衝起來,並會在最後的代碼塊中應用。因為我們需要一個強制的重排,通過以下方式可以實現: //移除has-transition類
$(this).removeClass(‘has-transition‘);
//修改屬性
$(this).css(‘margin-left‘, 100);
// 觸發強制重排,從而在class和屬性中的改變可以立刻生效
$(
this)[0].offsetHeight;// 樣本,其他的屬性也可以實現 // 增加has-transition類$(
this).addClass(‘has-transition‘); // 修改屬性$(
this).css(‘margin-left‘, 50); 現在將會按設想中的工作了。 實際開發中的最佳化建議 通過總結一些有用的資訊,(作者)提供以下建議:
- 建立有效html和css,但不要忘記明確文檔編碼。樣式可以添加到<head>中,指令碼則添加到<body>的尾部。
- 嘗試簡化和最佳化css選取器(使用CSS預先處理的開發人員通常不會注意這種最佳化方法),盡量保持低級的css嵌套。這也是css選取器標識效能的方法(從最快的那個開始)。
-
- 1、標識符 #id
- 2、類 .class
- 3、標籤: div
- 4、兄弟選取器:a+i
- 5、鄰接父子節點選取器 ul > li
- 6、通用選取器:*
- 7、特性選取器:input[type="text"]
- 8、偽類和虛擬元素:a:hover。你需要記住,瀏覽器處理CSS選取器的順序是從右至左,這就是為什麼最右邊的選取器應當是最快的那種,比如#id或.class
div * {...} 壞 .list li {...} 壞 .list-item {...} 好 #list .list-item {...} 好 1、在你的指令碼中,儘可能的減少對dom的操作,盡量緩衝那些會需要重複用到類型和對象。在執行複雜的操作時,比較好的方法是:操作一個“離線”的元素,並在結束後重新增加到DOM樹中(離線的元素即脫離了DOM後在記憶體中儲存的元素)。 2、如果使用jQuery來選擇元素,遵循jQuery 選取器最佳化實踐。 3、為了修改元素的樣式,修改元素的"class"屬性是通常會採用的方法。執行這種操作的DOM節點的層次越深越好。(同時也是由於深層次定義有利於邏輯解耦) 4、如果可以的話,僅對那些絕對位置或懸浮定位的元素執行動畫。 5、滾動時去除複雜的虛擬元素動畫(例如:hover,可以給body增加一個額外的沒有hover動畫的樣式)。 若需要更詳細的說明,請參見以下文章:
1. How browsers work
2. Rendering: repaint, reflow/relayout, restyle
希望本文能夠有所啟發!
30th June 2014