原文链接: mvvm 使用 fragment 模仿 html的编译
上一篇: vue cli3 使用cdn 减少打包体积 在index文件中加入script标签
下一篇: 使用 vue ui 创建 项目 并且安装webpack-bundle-analyzer 插件
MDN
https://developer.mozilla.org/zh-CN/docs/Web/API/DocumentFragment
DocumentFragment
,文档片段 接口,表示一个没有父级文件的最小文档对象。它被作为一个轻量版的 Document
使用,用于存储已排好版的或尚未打理好格式的XML片段。最大的区别是因为 DocumentFragment 不是真实DOM树的一部分,它的变化不会触发 DOM 树的( 重新渲染 ) ,且不会导致性能等问题。
最常用的方法是使用文档片段作为参数(例如,任何 Node
接口类似 Node.appendChild
和 Node.insertBefore
) 的方法),这种情况下被添加(append)或被插入(inserted)的是片段的所有子节点, 而非片段本身。因为所有的节点会被一次插入到文档中,而这个操作仅发生一个重渲染的操作,而不是每个节点分别被插入到文档中,因为后者会发生多次重渲染的操作。
该接口在Web组件中也非常有用: 模板 元素在其 HTMLTemplateElement.content
属性中包含了一个 DocumentFragment 。
可以使用 document.createDocumentFragment
方法或者构造函数来创建一个空的 DocumentFragment.
属性 节
该接口没有特殊的属性,其属性都继承自 Node
,并补充了 ParentNode
接口中的属性。
返回一个实时(live) HTMLCollection
,包含所有属于 DocumentFragment
的 元素
类型的子对象。
ParentNode.firstElementChild
只读
返回 DocumentFragment
的第一个 Element
类型的子对象,如果没有则返回 null
。
ParentNode.lastElementChild
只读
返回 DocumentFragment
的最后一个 Element
类型的子对象,如果没有则返回 null
。
ParentNode.childElementCount
只读
Returns an unsigned long
giving the amount of children that the DocumentFragment
has.
构造函数 节
返回空的 DocumentFragment
对象。
方法 节
该接口继承 Node
的全部方法, 并实现了 ParentNode
接口中的方法。
DocumentFragment.querySelector()
Returns the first Element
node within the DocumentFragment
, in document order, that matches the specified selectors.
DocumentFragment.querySelectorAll()
Returns a NodeList
of all the Element
nodes within the DocumentFragment
that match the specified selectors.
DocumentFragment.getElementById()
Returns the first Element
node within the DocumentFragment
, in document order, that matches the specified ID.
简单例子
原始html文档会在两秒后变化,使用fragment 在内存中模仿vdom,然后一次性挂载
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
setTimeout(
() => {
let ul = document.getElementById('ul')
// 使用fragment 在内存中修改后,在挂载到真实dom,这样比在dom中修改多次要快
let fragment = document.createDocumentFragment()
let child
while (child = ul.firstChild) {
console.log(child)
// 结点只能有一个父节点
// 当父节点变化后,父节点的子节点列表会自动更新,所以不会出现死循环
fragment.appendChild(child)
}
console.log(fragment)
Array.from(fragment.childNodes).forEach(
(node) => {
console.log(node)
// 如果是元素结点,将结点中文字替换
if (node.nodeType === Node.ELEMENT_NODE){
node.textContent = 'aaa'
}
}
)
ul.appendChild(fragment)
}, 2000
)
</script>
</body>
</html>
挂载之后,如果继续更新fragment中的结点,则dom也会实时重新渲染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
setTimeout(
() => {
let ul = document.getElementById('ul')
// 使用fragment 在内存中修改后,在挂载到真实dom,这样比在dom中修改多次要快
let fragment = document.createDocumentFragment()
let child
while (child = ul.firstChild) {
console.log(child)
// 结点只能有一个父节点
// 当父节点变化后,父节点的子节点列表会自动更新,所以不会出现死循环
fragment.appendChild(child)
}
console.log('child',fragment.childNodes)
let tmp = fragment.childNodes[1]
console.log('tmp',tmp)
Array.from(fragment.childNodes).forEach(
(node) => {
console.log(node)
// 如果是元素结点,将结点中文字替换
if (node.nodeType === Node.ELEMENT_NODE) {
node.textContent = 'aaa'
}
}
)
ul.appendChild(fragment)
setTimeout(
() => {
console.log(fragment.childNodes);
console.log('tmp',tmp)
tmp.textContent='aaaaaaaaaaaaaaaaaaa'
}, 1000
)
}, 1000
)
</script>
</body>
</html>