Dom:文档对象模型 。
HTML 的文档document页面是一切的基础,没有它dom就无从谈起。
当创建好一个页面并加载到浏览器时,DOM就悄然而生,它会把网页文档转换为一个文档对象,主要功能是处理网页内容。
我们通过操作dom(创建节点、删除节点、获取节点、插入节点)来实现对浏览器页面的更新。
对Dom的修改引起样式更迭:
很多时候,我们对 DOM 的操作都不会局限于访问,而是为了修改它。当我们对 DOM 的修改会引发它外观(样式)上的改变时,就会触发回流或重绘。
这个过程本质上还是因为我们对 DOM 的修改触发了渲染树(Render Tree)的变化所导致的:
-
回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。
-
重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘。
重绘不一定导致回流, 但回流一定会导致重绘。
提高dom速度(减少dom操作,避免过度渲染):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>DOM操作测试</title>
</head>
<body>
<div id="container"></div>
</body>
</html>
如果向container写入10000句一样的话(优化前)
for(var count=0;count<10000;count++){
document.getElementById('container').innerHTML+='<span>我是一个小测试</span>'
}
优化后:
let container = document.getElementById('container')
let content = ''
for(let count=0;count<10000;count++){
// 先对内容进行操作
content += '<span>我是一个小测试</span>'
}
// 内容处理好了,最后再触发DOM的更改
container.innerHTML = content
考虑JS 的运行速度,比 DOM 快得多这个特性。我们减少 DOM 操作的核心思路,就是让 JS 去给 DOM 分压。
这个思路,在 DOM Fragment 中体现得淋漓尽致。
DocumentFragment 接口表示一个没有父级文件的最小文档对象。它被当做一个轻量版的 Document 使用,用于存储已排好版的或尚未打理好格式的XML片段。因为 DocumentFragment 不是真实 DOM 树的一部分,它的变化不会引起 DOM 树的重新渲染的操作(reflow),且不会导致性能等问题。
let container = document.getElementById('container')
// 创建一个DOM Fragment对象作为容器
let content = document.createDocumentFragment()
for(let count=0;count<10000;count++){
// span此时可以通过DOM API去创建
let oSpan = document.createElement("span")
oSpan.innerHTML = '我是一个小测试'
// 像操作真实DOM一样操作DOM Fragment对象
content.appendChild(oSpan)
}
// 内容处理好了,最后再触发真实DOM的更改
container.appendChild(content)
可以看出,DOM Fragment 对象允许我们像操作真实 DOM 一样去调用各种各样的 DOM API,我们的代码质量因此得到了保证。并且它的身份也非常纯粹:当我们试图将其 append 进真实 DOM 时,它会在乖乖交出自身缓存的所有后代节点后全身而退,完美地完成一个容器的使命,而不会出现在真实的 DOM 结构中。这种结构化、干净利落的特性,使得 DOM Fragment 作为经典的性能优化手段大受欢迎,这一点在 jQuery、Vue 等前端框架的源码中均有体现。