那些被忽略但很好用的 Web API / CreateDocumentFragment

除了功能完善,有时候效能也该一并考虑。

今天要介绍的是 CreateDocumentFragment,它隶属於 document 物件底下,而 Document 所指的就是整个网页文件,也是节点树(DOM tree)的一个进入点。简单的说,Document 提供了一些 methods 让我们去操作整个网页文件,像是 document.querySelector 就是我们经常使用,拿来遍历整个 DOM tree 并找到我们指定的元素。


CreateDocumentFragment

先直接说明 CreateDocumentFragment 的功能好了,它可以帮我们创建一个 DocumentFragment,而DocumentFragment 就如同一个虚拟的文件片段,就算对其进行节点操作时并不会造成浏览器回流(reflow),也就是不会耗费资源来进行画面更新,这也就是它最大的优点。

如果你还没接触过前端框架的话,那相信你在遇到需要新增动态元素至画面上时你通常会有下面这几个方法:

# innerHTML

首先是比较基础的方式是用,也就是用字串的方式来设定某个元素的 HTML 内容,使用起来非常间单,不过如果想要塞入的内容结构上比较复杂的话那就会非常麻烦了(字串里可是没有编辑器提示的啊~)。

let list = ["egg", "milk", "sugar", "flour"];
let htmlContent = "";
for (let i = 0; i < list.length; i++) {
  htmlContent += `<li>${list[i]}</li>`;
};
const ul = document.querySelector("ul");
ul.innerHTML += htmlContent;

还有一点比较麻烦的是,在父元素已经有部分子元素情况下使用 innerHTML,会很难从中插入新的元素,会需要其他额外的处理,反而会造成程序码很冗长,所以它大部分都不会是我们的最佳解决方案。

<ul>
  <li>我是本来就在的元素</li>
  <li>我是本来就在的元素</li>
  <!--   如果你的新元素要放在这里就会很麻烦 -->
  <li>我是本来就在的元素</li>
  <li>我是本来就在的元素</li>
</ul>

 

# appendChild

相比 innerHTMLappendChild 应该才是更常使用的方法:

let list = ["egg", "milk", "sugar", "flour"];
const ul = document.querySelector("ul");
for (let i = 0; i < list.length; i++) {
  const li = document.createElement("li");
  li.textContent = list[i]
  ul.appendChild(li);
};

这种方式就算想要创建比较复杂的 HTML 结构也可以写得比较有系统性,程序码也有比较好的易读性及维护性,而且其实除了 appendChild,你也可以用 insertBefore 选择新元素要注入的位置。

<ul>
  <li>我是本来就在的元素</li>
  <!--  新元素将会放在这边 -->
  <li>我是本来就在的元素</li>
</ul>
let list = ["egg", "milk", "sugar", "flour"];
const ul = document.querySelector("ul");
const last_li = ul.querySelector("li:last-child");
for (let i = 0; i < list.length; i++) {
  const new_li = document.createElement("li");
  new_li.textContent = list[i]
  ul.insertBefore(new_li, last_li);
};

不过这样的方式有个致命的缺点,每次回圈在执行时都会直接在目前的 DOM tree 中塞入新元素时,此时会促使网页更新画面,这样其实或多或少会引响效能。

 

# createDocumentFragment

但如果使用 createDocumentFragment 可就不一样了,我们是在一个虚拟的文件片段中加入新元素,这样就完全不会造成画面的更新,因为它根本就没渲染在画面上。

let list = ["egg", "milk", "sugar", "flour"];
const ul = document.querySelector("ul");
const fragment = document.createDocumentFragment()
for (let i = 0; i < list.length; i++) {
  const li = document.createElement("li");
  li.textContent = list[i]
  fragment.appendChild(li);
};
ul.appendChild(fragment)

当我们将全部想要新增的元素都加进 DocumentFragment 後,再一次性的塞进我们真正想要放的位置就可以了,整体的感觉会很像 innerHTML,但 innerHTML 在设定後其实还多了一个字串转换成节点的过程,所以 createDocumentFragment 在效能层面来说真的是比较友善。

不过现在的浏览器似乎对於短时间内的连续 DOM 操作都有做一些最佳化,不至於让效能差到太多,但使用 CreateDocumentFragment 其实也可以让 「不断插入」 变成 「一次插入」,在程序码的目的表达上也会更清楚。

 

今天介绍的 CreateDocumentFragment 虽然不是什麽功能华丽的 API,但是非常建议大家可以使用看看,尽管能提升的效能或许不多,但这一点点很有可能就是你跟其他人的差异喔~


<<:  Day4 跟着官方文件学习Laravel-CSRF保护

>>:  Day 05 - 想要够给力的机器-EC2

[Day 24] 字形渲染(Text Rendering) - 渲染文字

今日目标 在视窗内渲染出"Hello, world!" DrawText voi...

Day10 Pandas模组二

今天的影片为接续上一部的内容,以及介绍几个简单的统计函数 (还有短短的英文小教室...我要去跟英文老...

【这些年我似是非懂的 Javascript】Day 29 - 物件 # Part 5 # 特性存取的秘密

今天要来分享特性存取的秘密~ [[GET]] 你知道当你在存取一个物件里面的特性时会发生什麽事情吗...

D5-用 Swift 和公开资讯,打造投资理财的 Apps { 实作 上市/上柜/兴柜 所有资料的列表 }

写到第五天,开始写 UI 罗~~ 前面都是在做资料处理,所以只有程序码,没有 UI 画面,谢谢看到今...