关于document.createDocumentFragment()(转)

documentFragment 是一个无父对象的document对象.

他支持以下DOM2方法:

appendChild, cloneNode, hasAttributes, hasChildNodes, insertBefore, normalize, removeChild, replaceChild.

也支持以下DOM2属性:

attributes, childNodes, firstChild, lastChild, localName, namespaceURI, nextSibling, nodeName, nodeType, nodeValue, ownerDocument, parentNode, prefix, previousSibling, textContent.

其他方法可以将documentFragment 作为一个参数,(比如Node的 appendChild和insertBefore方法),这样,fragment 就可以被追加到父对象中。

var frag = document.createDocumentFragment();
frag.appendChild(document.createTextNode('Ipsum Lorem'));
document.body.appendChild(frag); 

document.createDocumentFragment()说白了就是为了节约使用DOM。每次JavaScript对DOM的操作都会改变页面的变现,并重新刷新整个页面,从而消耗了大量的时间。

为解决这个问题,可以创建一个文档碎片,把所有的新节点附加其上,然后把文档碎片的内容一次性添加到document中。

var oui=document.getElementById("oItem");
for(var i=0;i<10;i++){
  var oli=document.createElement("li");
  oui.appendChild(oli);
  oli.appendChild(document.createTextNode("Item"+i));
} 

上面的代码在循环中调用了oui.appendChild(oli),每次执行这条语句后,浏览器都会更新页面。其次下面的oui.appendChild()添加了文本节点,也要更新页面。所以一共要更新页面20次。

为了页面的优化,我们要尽量减少DOM的操作,将列表项目在添加文本节点之后再添加,并合理地使用creatDocumentFragment(),代码如下:

var oui=document.getElementById("oItem");
var oFragment=document.createDocumentFragment();
for(var i=0;i<10;i++){
  var oli=document.createElement("li");
  oli.appendChild(document.createTextNode("Item"+i));
  oFragment.appendChild(oli);
}
oui.appendChild(oFragment); 

测试页面案例

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>document.createDocumentFragment()测试页面</title>
</head>
<body>
<script type="text/javascript">
       var d1 = new Date();
       for(var i=0;i<1000;i++){
              var op=document.createElement("P");
              var oText=document.createTextNode("test1");
              op.appendChild(oText);
              document.body.appendChild(op);
       }

       var d2 = new Date();
       document.write("方法一用时:" + (d2.getTime() - d1.getTime()) + "<br/>");
       //---+-----

       var d3 = new Date();
       var oFrag=document.createDocumentFragment();
       for(var i=0;i<1000;i++){
              var op=document.createElement("P");
              var oText=document.createTextNode("test2");
              op.appendChild(oText);
              oFrag.appendChild(op);
       }

       document.body.appendChild(oFrag);
       //这段代码中
       var d4 = new Date();
       document.write("方法二用时:" + (d4.getTime() - d3.getTime()) + "<br/>");
</script>
</body>
</html>
View Code

Firefox       方法一用时:11

                    方法二用时:7

转载自: http://www.aspxhome.com/javascript/start/20094/86925.htm

<think>我们正在处理一个关于Vue3的问题,具体是使用`createApp`挂载组件到`document.createDocumentFragment()`时,`console.log(popup.$el)`输出为空字符串。我们首先需要理解这个问题的原因。在Vue3中,当我们使用`createApp`创建一个应用实例,并挂载到一个DOM元素上时,该应用的根组件实例会有一个`$el`属性,指向挂载的DOM元素。然而,当我们挂载到一个`DocumentFragment`时,情况会有所不同。`DocumentFragment`是一个轻量级的文档对象,它不会属于主文档树,通常用于存储DOM节点,然后一次性添加到文档中。但是,当我们把Vue应用挂载到`DocumentFragment`时,Vue会使用这个片段作为容器。然而,`DocumentFragment`本身并不是一个真正的DOM元素(它没有标签名,也没有子节点会被认为是挂载的根元素),所以在挂载后,`$el`属性可能会指向一个文本节点或者甚至为空。具体原因可能是由于Vue在挂载到`DocumentFragment`时,会替换掉容器(即`DocumentFragment`)内的内容,但`DocumentFragment`本身并不具有子节点,所以导致`$el`可能无法正确引用。让我们通过一个示例代码来重现问题:```javascriptimport{createApp}from'vue'importIotPopupDetailfrom'./IotPopupDetail.vue'//创建一个DocumentFragmentconstfragment=document.createDocumentFragment()//创建应用并挂载到fragmentconstpopupApp=createApp(IotPopupDetail)constpopup=popupApp.mount(fragment)console.log(popup.$el)//输出为空字符串或undefined```在Vue3中,`mount`方法可以接受一个DOM元素或一个选择器字符串。但是,它也可以接受一个`DocumentFragment`吗?实际上,根据Vue3的源码,`mount`方法会调用DOMAPI来将应用渲染到容器中。但是,`DocumentFragment`不是一个元素节点,因此Vue在挂载时可能会创建一个空的文本节点作为占位,或者直接覆盖`DocumentFragment`的内容,但这样可能导致`$el`无法正确指向。另一种思路是,Vue在挂载时,会用自己的根DOM节点替换容器内的所有内容。但`DocumentFragment`本身没有子节点,所以替换后,`DocumentFragment`中会有一个根组件的DOM节点。然而,当我们尝试访问`popup.$el`时,由于`DocumentFragment`不是真实DOM树的一部分,所以它引用的节点可能没有被正确设置。在Vue3的源码中,组件的`$el`属性实际上被定义为组件实例的根DOM节点。当挂载到`DocumentFragment`时,这个根节点确实存在,但它是在`DocumentFragment`内部的。因此,理论上应该可以访问到。但是,为什么输出为空字符串呢?我们注意到,当我们打印一个DOM节点时,如果它是一个元素节点,那么`$el`会输出该元素的字符串表示(例如`[objectHTMLDivElement]`)。但如果它不是一个元素节点,比如是一个文本节点,那么可能输出的是文本内容(如果为空就是空字符串)。或者,如果Vue在挂载过程中因为容器是`DocumentFragment`而无法正确创建根元素,那么可能会导致`$el`为空。实际上,Vue3支持挂载到`DocumentFragment`吗?根据Vue3的文档,`mount`方法的容器参数应该是一个实际的DOM元素,而不是`DocumentFragment`。因此,挂载到`DocumentFragment`可能是不被支持的,或者行为是未定义的。因此,解决方案可能是避免使用`DocumentFragment`作为挂载容器,而使用一个实际的DOM元素,例如一个`div`。但是,如果我们确实需要将组件挂载到一个不在文档中的元素(以便稍后插入文档),我们可以创建一个临时的div作为容器,挂载后,再将组件的根元素移动到`DocumentFragment`中。示例:```javascriptconstcontainer=document.createElement('div')constpopupApp=createApp(IotPopupDetail)constpopup=popupApp.mount(container)//现在popup.$el指向container内的根DOM节点console.log(popup.$el)//这里会正常输出//然后我们可以将popup.$el移动到DocumentFragmentconstfragment=document.createDocumentFragment()fragment.appendChild(popup.$el)//或者直接插入到文档中的某个位置document.body.appendChild(fragment)```这种方法确保我们挂载在一个实际的DOM元素上,所以`$el`能够正确指向组件的根元素,然后我们再将这个根元素移动到`DocumentFragment`中,这样就不会影响使用。所以,总结问题的根本原因是:Vue3的挂载容器要求是一个实际DOM元素,而`DocumentFragment`不是,所以导致`$el`无法正确设置。解决方案是使用一个临时的实际DOM元素进行挂载,然后再将生成的根节点移到`DocumentFragment`中。希望这个解释能帮助你解决问题。</think> ### Vue3 中 createApp 挂载到 DocumentFragment 的 $el 为空问题分析 #### 问题根本原因 在 Vue 3 中,`$el` 代表组件实例的根 DOM 元素,但当你将组件挂载到 `document.createDocumentFragment()` 时: 1. DocumentFragment 是轻量级的虚拟 DOM 容器,**不会被渲染为真实 DOM 节点** 2. Vue 会以 DocumentFragment 作为容器进行渲染,**但该片段本身不是组件根元素** 3. `$el` 特性只存在于渲染为**真实 DOM 元素**的组件中 #### 解决方案(使用 Vue 兼容方式) 使用 Vue 推荐的 `createVNode` + `render` API 实现虚拟节点挂载: ```typescript import { createVNode, render } from 'vue' import IotPopupDetail from './IotPopupDetail.vue' const fragment = document.createDocumentFragment() // 创建虚拟节点 const vnode = createVNode(IotPopupDetail) // 渲染到 DocumentFragment render(vnode, fragment) // 正确获取组件实例的根元素 const rootElement = vnode.el as HTMLElement console.log(rootElement) // 正常输出 DOM 元素 ``` #### 替代方案(使用真实容器) 创建临时真实 DOM 容器作为过渡方案: ```javascript const container = document.createElement('div') const popup = createApp(IotPopupDetail).mount(container) console.log(popup.$el) // 正确输出 DOM 元素 // 获取组件渲染结果并移到文档片段 const fragment = document.createDocumentFragment() fragment.appendChild(popup.$el) // 插入到真实 DOM 中 document.body.appendChild(fragment.cloneNode(true)) ``` #### 关键原理说明 1. Vue 的 `$el` 只存在于**实际渲染到页面 DOM 树**的组件中 2. DocumentFragment 作为虚拟容器时,其内容需要通过 `render` 函数返回的 VNode 访问 3. Vue 3 的 Composition API 鼓励使用 `createVNode` 替代直接操作 `$el`[^1] #### 使用建议 ```mermaid graph TD A[创建组件] --> B{需要渲染到页面?} B -->|是| C[使用真实 DOM 挂载] B -->|否| D[使用 createVNode + render] D --> E[通过 vnode.el 访问] C --> F[通过 $el 访问] ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值