定义和用法
createdocumentfragment()方法创建了一虚拟的节点对象,节点对象包含所有属性和方法。当你想提取文档的一部分,改变,增加,或删除某些内容及插入到文档末尾可以使用
你也可以使用文档的文档对象来执行这些变化,但要防止文件结构被破坏,createDocumentFragment() 方法可以更安全改变文档的结构及节点
在手写 vue 响应式原理中
// 利用正则表达式来进行查找使用过插值表达式的节点
const reg = /\{\{(.+)\}\}/
class Compiler {
constructor(el, vm) {
this.el = document.querySelector(el)
this.vm = vm
// 进行遍历标签节点,查找出使用插值表达式的 DOM 节点
this.frag = this._createFragment()
// 由于上面使用 appendChid 方法将原 dom 树中的节点添加到 DocumentFragment 中会导致节点丢失,所以要把 frag 添加到 el 中
this.el.appendChild(this.frag)
}
_createFragment 的实现
_createFragment() {
// 创建虚拟节对象(在内存中)
const frag = document.createDocumentFragment()
let child
// 遍历 el 上的所有标签的 DOM 对象
while (child = this.el.firstChild) {
this._compile(child)
frag.appendChild(child)
}
// 因为 createDocumentFragment 在内存中创建节点,会导致每添加添加都会丢失之前的 child,所以用 frag 来保存并返回
return frag
}
_compile 的实现
// 进行遍历节点属性来查询使用过插值表达式 {{}} 和 v-model 的节点
// 如果节点 nodeType === 1 就是标签属性
// 如果节点 nodeType === 3 就是文本属性
_compile(node) {
// 判断当前节点循环出的是不是标签节点
if (node.nodeType === 1) {
const attrs = node.attributes
if (attrs.hasOwnProperty('v-model')) {
// 当前节点属性是 v-model
const name = attrs['v-model'].nodeValue
node.addEventListener('input', e => {
this.vm[name] = e.target.value
})
}
}
// 判断当前节点循环出的是不是文本节点
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
const name = RegExp.$1.trim()
new Watcher(node, name, this.vm)
}
}
}
发现 createDocumentFragment 居然有如此妙用,以上只是部分代码,由于有bug,就不发了(尴尬)