文档片段如何工作?

how Document Fragment works?

任何人都可以简要解释一下 documentFragment 的实际作用吗?我一直在寻找一个明确的解释,但直到现在我才得到任何解释。

我读到的是,documentFragment 类似于 DOM 结构,我们可以在其中添加修改 DOM 元素而不中断文档的实际流程。

我也看过,documentFragment 比将每个元素一个一个地附加到 DOM 更快。我觉得,documentFragment 不会每次都重新计算样式,所以速度更快。

我有两个例子,

以片段方式进行:

var frag = document.createDocumentFragment();
var div1 = document.createElement("div");
var div2 = document.createElement("div");
frag.appendChild(div1);
frag.appendChild(div2);
document.getElementById("someId").appendChild(frag);

以正常方式进行:

var div = document.createElement("div");
var div1 = document.createElement("div");
var div2 = document.createElement("div");
div.appendChild(div1);
div.appendChild(div2);

document.getElementById("someId").appendChild(div);

上面两个例子到底发生了什么?

"the fragment way" 和 "the normal way" 之间有一个重要的区别:

使用document.createElement

const div = document.createElement('div');
div.appendChild(document.createTextNode('Hello'));
div.appendChild(document.createElement('span'));
document.body.appendChild(div);
console.log(div.childNodes); // logs a NodeList [#text 'Hello', <span>]

这导致以下 DOM 结构:

<body>
  <div>
    Hello
    <span></span>
  </div>
</body>

使用DocumentFragment

const frag = document.createDocumentFragment();
frag.appendChild(document.createTextNode('Hello'));
frag.appendChild(document.createElement('span'));
document.body.appendChild(frag);
console.log(frag.childNodes); // logs an empty NodeList

这导致以下 DOM 结构:

<body>
  Hello
  <span></span>
</body>

这意味着使用 DocumentFragment 的实例调用 appendChildinsertBefore 会将文档片段的子节点移动到新的父节点。之后文档片段为空

正如您正确提到的那样,创建文档片段并向其附加多个元素比将这些元素一个一个地附加到真正的 DOM 更有效,这会导致浏览器重新-每次渲染页面的一部分。因为文档片段的内容在屏幕上是不可见的,所以页面只需要重新渲染一次。

每当您创建大型 DOM 结构时,建议在文档片段中创建它并在完成后将其附加到 DOM。

对于仅附加 2 个子项,您不会看到任何性能问题。想象一下,您有一个包含 100 个项目的书籍数组,您想要将它们附加到 DOM。你会写这样的代码:

let books=[,,,,,,,,,,,,,,,,]
let bookList;
document.addEventListener('DOMContentLoaded',load)
 function load(){
    bookList=document.getElementById('books')
    books.forEach(book=>{
        let li=document.createElement('li')
        li.textContext=book
        li.class="bookItem"
        // here is the headache part
        bookList.appendChild(li)     
    }))
   }

所以你要循环 100 次,每次,你都要告诉浏览器重绘屏幕。这将占用大量资源。根据您使用的系统,您可能会看到屏幕闪烁。

对于片段我会这样写load

function load(){
     bookList=document.getElementById('books')
     let df=new DocumentFragment()
     books.forEach(book=>{
         let li=document.createElement('li')
         li.textContext=book
         li.class="bookItem"
         // we are appending to df not to the bookList
         df.appendChild(li)     
    })
    // I collected all list elements inside DocumentFragment
    // Now I append it to the bookList
    bookList.appendChild(df)
  }

创建 document fragment 就像创建 div。但它不会向页面添加任何 Html。您不会在 HTML 源代码中看到:

 <DocumentFragment></DocumentFragment>

它只是一个空容器,用于容纳 Html 的其他部分。可以在单个操作中注入和克隆片段,而不必注入和克隆每个片段 单个节点一遍又一遍。我们正在以更好的性能实现完全相同的目标。