DocumentFragment 的優化小知識

混元霹靂手發表於2017-05-07

作者 : 混元霹靂手-ziksang

無論在做專案還是學知識,當看到DocumentFragment 這個文件碎片的時候,你並不會去在意他,往往就是這東西就能看的出你對Js的認識,和一個層級的化分,前天我看到一位小兄弟發了一篇文章,上面寫著阿里十道面試題,裡面有一個問題是這樣的

3.請把<body><p>第1行</p><p>第2行</p>...</body>(body之間有100個p元素)插入body裡面,注意:需要考慮到效能問題。

對於想當然的想法

       for(var i=0;i<100;i++)
       {
              var op=document.createElement("p");
              var oText=document.createTextNode(i);
              op.appendChild(oText);
              document.body.appendChild(op);
       }複製程式碼

我們for大法進行迴圈建立一百次p無素,很顯示,每次JavaScript對DOM的操作是一個很消耗效能的問題,頁且每次進行appendchild插入節點的時候,會導致頁面進行重繪(paining),對(layout)重新佈局,那這個操作說明建立了100次節點,重繪了100次,暫且不談,很顯然,效能問題出來了。那面試的小夥是如何回答的這個問題的?

DocumentFragment  的優化小知識

對於這個回答,效能問題是解決了,是減少了dom的操作,如果我是面試官只給50分,那如何拿到更高的分數,其實本質上我想的是面試官肯定考的不是通過這個知識點來解決此問題,我把程式碼從新寫一遍

        var lis = "",ul = document.createElement("ul");
        //把li以字串形式生成
        for(var i = 1; i <= 100; i++) {    lis += "<li>第" + i + "行</li>";}// 最後通過innerHTML插入ul裡面
        ul.innerHTML = lis;
        //這裡才操作dom,把ul插入到body
        document.body.appendChild(ul);複製程式碼

此方法只進行了一次dom操作,和一次頁面重繪,是一個好辦法,但是我覺得這個方法有點硬湊的感覺,沒有把dom的知識點掌握全,而且這種寫法對後續的擴充和維護性是不存在的,也無法封裝成一個方法或者結合到任何框架中去

DocumentFragment

然後我想到這東西,無論是jquery還是vue都使用了這玩意文件碎片來進行dom操作dom級別的優化

DocumentFragment介面表示沒有父級的最小文件物件。它被用作輕量級版本,Document以像標準文件一樣儲存由節點組成的文件結構的片段。關鍵區別在於,由於文件片段不是實際DOM結構的一部分,它是一個虛擬的dom節點,存在於記憶體中,所以對片段所做的更改不會影響文件,導致迴流,或者在進行更改時可能會發生任何效能影響。

一個常見的用途DocumentFragment是建立一個,在其中組裝一個DOM子樹,然後使用Node諸如appendChild()或(或insertBefore())之類的介面方法將該片段附加或插入到DOM中。這樣做會將片段的節點移動到DOM中,留下空白DocumentFragment。因為所有的節點都被一次性插入到文件中,所以如果單獨插入,則每個節點只會觸發一個迴流和渲染,而不是每個節點的潛在一個

建立DocumentFragment ->document.createDocumentFragment()

 var oFrag=document.createDocumentFragment();
       for(var i=0;i<100;i++)
       {
              var op=document.createElement("P");
              var oText=document.createTextNode(i);
              op.appendChild(oText);
              oFrag.appendChild(op);
       }
       document.body.appendChild(oFrag);複製程式碼

很明顯,它有一種特殊的行為,該行為使得它非常有用,即當請求把一個DocumentFragment 節點插入文件樹時,插入的不是 DocumentFragment 自身,而是它的所有子孫節點。這使得DocumentFragment 成了有用的佔位符,暫時存放那些一次插入文件的節點。它還有利於實現文件的剪下、複製和貼上操作。

此時我們操作了兩次dom,和一次重繪,為了建立文件碎片我們多操作了一次dom換來了100次dom建立的優化,並沒有什麼問題,也不用糾結這個問題。此時只是把documentFragment的子節點新增到了body節點裡,並沒有把整個文件碎片加入進去。

當文件碎片插入完自動會被銷燬碎片內容

這個細節我相信大家肯定沒有怎麼關注過

       var oFrag=document.createDocumentFragment();
       for(var i=0;i<100;i++)
       {
              var op=document.createElement("P");
              var oText=document.createTextNode(i);
              op.appendChild(oText);
              oFrag.appendChild(op);
       }
       document.body.appendChild(oFrag);
       for(var i=0;i<100;i++)
       {
              var op=document.createElement("P");
              var oText=document.createTextNode(i);
              op.appendChild(oText);
              oFrag.appendChild(op);
       }
       //這段程式碼中
       document.body.appendChild(oFrag);複製程式碼

你執行程式碼你會發現0-99,0-99,說明了什麼?說明了只要文件碎片一但被插入後,會進行一個碎片回收,清除,但是文件碎片容器還是存在的,我們不用再次重新再進行一次dom操作來進行建立文件碎片

移除dom到文件碎片中

如果將文件中的節點新增到文件碎片中,就會從文件中移除該節點,也不會從瀏覽器再看到該節點,新增到文件碎片的新節點也不屬於文件樹

<html>
<head>
  <meta charset="UTF-8">
  <title>v-circle</title>
</head>
<body>
<div>
    <ul>
        <li>1</li>
        <li>2</li>
    </ul>
</div>
    <script type="text/javascript">

       var oFrag=document.createDocumentFragment();
       var ul = document.getElementsByTagName('ul')[0]
       console.log(ul)
       oFrag.appendChild(ul)
    </script>
</body>
</html>複製程式碼

此時我把DOM樹中的ul節點新增到文件碎片中,此時dom樹中的ul節點也不見了,跑到了文件碎片中

DocumentFragment  的優化小知識

你也可以很明顯的看的出,文件碎片不存在於dom樹中。所以上所有的操作不會重繪整個頁面

jquery作者John Resig給了效能測試報告

DocumentFragment  的優化小知識

雖然在2008年給出的報告,對於現代瀏覽器中,此效能差距不是很大,100次可能算不上什麼,但對於前端大資料的時代裡,還是很有必要的。

歡迎指正!!!!!!

想了解更多知識歡迎訂閱我的掘金專欄

渣渣前端開發工程師,喜歡鑽研,熱愛分享和講解教學請 微信 zzx1994428 QQ494755899

如果轉載請標註出自@混元霹靂手ziksang

相關文章