瀏覽器渲染頁面過程描述,DOM編程技巧以及重排和重繪。

1、瀏覽器渲染頁過程描述html

一、瀏覽器解析html源碼,而後建立一個DOM樹。ajax

在DOM樹中,每個HTML標籤都有一個對應的節點(元素節點),而且每個文本也都有一個對應的節點(文本節點)。編程

DOM樹的根節點就是documentElement,對應的是html標籤。數組

二、瀏覽器解析CSS代碼,計算出最終的樣式數據。瀏覽器

對CSS代碼中非法的語法它會直接忽略掉。緩存

解析CSS的時候會按照以下順序來定義優先級:瀏覽器默認設置,用戶設置,外聯樣式,內聯樣式,html中的style(嵌在標籤中的樣式)。數據結構

三、建立完DOM樹並獲得最終的樣式數據以後,構建一個渲染樹。app

渲染樹和DOM樹有點像,可是有區別。DOM樹徹底和html標籤一一對應,而渲染樹會忽略不須要渲染的元素(head、display:none的元素)。佈局

渲染樹中每個節點都存儲着對應的CSS屬性。性能

四、當渲染樹建立完成以後,瀏覽器就能夠根據渲染樹直接把頁面繪製到屏幕上。

2、高性能Javascript DOM編程

咱們知道,用腳本進行DOM操做的代價很昂貴。把DOM和ECMAScript各自想象爲一個島嶼,它們之間用收費橋樑連接,ECMAScript每次訪問DOM,都要通過這座橋,並交納「過橋費」,訪問DOM的次數越多,費用也就越高。所以,推薦的作法是儘可能減小過橋的次數,努力呆在ECMAScript島上。這是比喻很是形象。那麼怎樣才能高效呢?

一、DOM訪問與修改

訪問DOM元素是有代價的(這裏其實和ajax調後臺數據接口是同樣,DOM是用於操做XML和HTML文檔的應用程序接口,一次能完成的事不要請求屢次),經過DOM獲取元素以後,修改元素的代價更是昂貴,由於它會致使瀏覽器從新計算頁面的幾何變化(重排和重繪)。

例子:

var times = 15000;

//code1

console.time('time1');

for(var i=0; i<times; i++){

  document.getElementById('myDiv1').innerHTML +='a';

}

console.timeEnd('time1');

//code2

console.time('time2');

var str = '';

for(var i=0; i<times; i++){

  str += 'a';

}

document.getElementById('maDiv2').innerHTML = str;

console.timeEnd('time2');

結果time1:2352.064ms/time2:0.789ms

第一段代碼的問題在於,每次循環迭代,改元素都會被訪問兩次:一次讀取innerHTML的值,另外一次重寫它。

結果充分證實,訪問DOM的次數越多,代碼的運行速度越慢。

所以,能減小DOM訪問的次數就儘可能減小,儘可能留在ECMAScript這端處理。

二、html集合&遍歷DOM

操做DOM另外一個耗能點就是遍歷DOM,在平時獲取一組元素的時候(getElementsByTagName方法),收集的結果是一個類數組對象,它處於一種「實時狀態」實時存在,這意味着當底層文檔對象更新時,它也會自動更新。

例子:

而這正是低效之源!很簡單,跟數組的優化操做同樣,緩存一個length變量就OK了(讀取一個集合的length比讀取一個普通數組的length要慢不少,由於每次都要查詢):

console.time('time0');

var lis0 = document.getElementsByTagName('li');

var str0 = '';

for(var i=0; i<lis0.length; i++){

  str0 +=lis0[i].innerHTML;

}

console.timeEnd('time0');

console.time('time1');

var lis1 = document.getElementsByTagName('li');

var str1 = '';

for(var i=0; len=lis1.length; i<len; i++){

  str1 += lis1[i].innerHTML;

}

console.timeEnd('time1');

結果:time0:0.237ms/time1:0.099ms

當獲取的這個類數組對象長度值特別大的時候(如1000),性能提高仍是很明顯的。

第一部分主要提了兩點優化,一是儘可能減小DOM的訪問,把運算放在ECMAScript這一端,二是儘可能緩存局部變量,好比類數組對象的length。

3、重排和重繪

一、什麼是重排和重繪

瀏覽器下載完頁面中的全部組件(html標記、Javascript、CSS、圖片)以後會解析生成兩個內部數據結構——DOM樹和渲染樹。

DOM樹表示頁面結構,渲染樹表示DOM節點如何顯示。DOM樹中的每個須要顯示的節點在渲染樹中至少存在一個對應的節點(隱藏的DOM元素 display:none 在渲染樹中沒有對應的節點)。

渲染樹中的節點被稱爲‘幀’或‘盒’,符合CSS模型的定義,頁面元素爲一個具備填充,邊距,邊框和位置的盒子。一旦DOM樹和渲染樹構建完成,瀏覽器就開始繪製頁面元素。

當DOM的變化影響了元素的幾何屬性(寬或高),瀏覽器須要從新計算元素的幾何屬性,同時其餘元素的幾何屬性和位置也會受到影響。瀏覽器會使渲染樹中受到影響的部分失效,並從新構造渲染樹,這個過程稱爲重排。完成重排後,瀏覽器會從新繪製受到影響的部分到屏幕,這個過程稱爲重繪。

因爲瀏覽器的流佈局,對渲染樹的計算一般只須要遍歷一次就能夠完成。table及其內部元素除外,它可能須要屢次計算才能肯定好其在渲染樹中節點的屬性,一般要花三倍同等元素的時間。這也是爲何咱們要避免使用table作佈局的一個緣由。

並非全部的DOM變化都會影響幾何屬性,好比改變一個元素的背景色並不會影響元素的寬和高,這種狀況下只會發生重繪。

二、重排什麼時候發生

a、添加或刪除可見的DOM元素

b、元素位置改變

c、元素尺寸改變

d、元素內容改變

e、頁面渲染初始化

f、瀏覽器窗口尺寸改變

三、渲染樹變化的排隊和刷新

獲取佈局信息的操做會致使隊列刷新,好比:

a、offsetTop,offsetLeft,offsetWidth,offsetHeight

b、scrollTop,scrollLeft,scrollWidth,scrollHeight

c、clientTop,clientLeft,clientWidth,clientHeight

d、getComputeStyle()||currentStyle(IE)

由於offsetHeight屬性須要返回最新的佈局信息,所以瀏覽器不得不執行渲染隊列中的「待處理變化」並觸發重排以返回正確的值(即便隊列中 改變的樣式屬性和想要獲取的屬性值並無什麼關係)。

四、最小化重排和重繪

改變元素多種樣式的時候,最好用className,一次性完成操做,這樣只會修改一次DOM。

五、文檔碎片

文檔碎片的設計初衷就是爲完成這類任務——更新和移動節點。當你附加一個片斷到節點時,實際上被添加的是該片斷的子節點,而不是片斷自己。只會觸發一次重排,並且只訪問一次實時的DOM。

用法:

var oFragment = document.createDocumentFragment();

for(var i=0; i<1000; i++){

  var oP = document.createElement('p');

  oP.innerHTML = i;

  oFragment.appendChild(oP);

}

var oDo = document.body;

oDo.appendChild(oFragment);

六、讓動畫脫離文檔流

使用絕對位置定位頁面上的動畫元素,將其脫離文檔流。這樣不會致使文檔流中的元素受到影響,不會大規模的進行重排和重繪。

第二部分重排和重繪是DOM編程中耗能的主要緣由之一,爲了不沒必要要的性能損耗能夠參考一下幾點:

一、儘可能不要在佈局信息改變時作查詢(會致使渲染隊列強制刷新);

二、同一個DOM的多個屬性改變能夠寫在一塊兒(減小DOM訪問,同時把強制渲染隊列刷新的風險降爲0);

三、若是要批量添加DOM,能夠先讓元素脫離文檔流,操做完成後帶人文檔流,這樣只會觸發一次重排(fragment元素的應用);

四、將須要屢次重排的元素,添加定位屬性,設置爲absolute,fixed,這樣此元素就脫離了文檔流,不會影響其餘元素。

相關文章
相關標籤/搜索