2.4 jQuery.buildFragment( args, nodes, scripts )
2.4.1 实现原理
方法jQuery.buildFragment( args, nodes, scripts )先创建一个文档片段DocumentFragment,然后调用方法jQuery.clean( elems, context, fragment, scripts )将HTML代码转换为DOM元素,并存储在创建的文档片段中。
文档片段DocumentFragment表示文档的一部分,但不属于文档树。当把Document
Fragment插入文档树时,插入的不是DocumentFragment自身,而是它的所有子孙节点,即可以一次向文档树中插入多个节点。当需要插入大量节点时,相比于逐个插入节点,使用ocumentFragment一次插入多个节点,性能的提升会非常明显。
此外,如果HTML代码符合缓存条件,方法jQuery.buildFragment()还会把转换后的DOM元素缓存起来,下次(实际上是第三次)转换相同的HTML代码时直接从缓存中读取,不需要重复转换。
方法jQuery.buildFragment()同时为构造jQuery对象和DOM操作提供底层支持,DOM操作将在第11章介绍和分析。
2.4.2 源码分析
方法jQuery.buildFragment( args, nodes, scripts )执行的5个关键步骤如下:
1)如果HTML代码符合缓存条件,则尝试从缓存对象jQuery.fragments中读取缓存的DOM元素。
2)创建文档片段DocumentFragment。
3)调用方法jQuery.clean( elems, context, fragment, scripts )将HTML代码转换为DOM元素,并存储在创建的文档片段中。
4)如果HTML代码符合缓存条件,则把转换后的DOM元素放入缓存对象jQuery.fragments。
5)最后返回文档片段和缓存状态{ fragment: fragment, cacheable: cacheable }。
下面来看看该方法的源码实现。
1.?定义jQuery.buildFragment( args, nodes, scripts )
相关代码如下所示:
6085 jQuery.buildFragment = function( args, nodes, scripts ) {
第6085行:定义方法jQuery.buildFragment( args, nodes, scripts ),它接受3个参数:
参数args:数组,含有待转换为DOM元素的HTML代码。
参数nodes:数组,含有文档对象、jQuery对象或DOM元素,用于修正创建文档片段DocumentFragment的文档对象。
参数scripts:数组,用于存放HTML代码中的script元素。方法jQuery.build Fragment()会把该参数传给方法jQuery.clean(),后者把HTML代码转换为DOM元素后,会提取其中的script元素并存入数组scripts。在11.2节会看到,方法.domManip
( args, table, callback )把转换后的DOM元素插入文档树后,会手动执行数组scripts中的元素。
2.?定义局部变量,修正文档对象doc
相关代码如下所示:
6086 var fragment, cacheable, cacheresults, doc,
6087 first = args[ 0 ];
6088
6089 // nodes may contain either an explicit document object,
6090 // a jQuery collection or context object.
6091 // If nodes[0] contains a valid object to assign to doc
6092 if ( nodes && nodes[0] ) {
6093 doc = nodes[0].ownerDocument || nodes[0];
6094 }
6095
6096 // Ensure that an attr object doesn't incorrectly stand in as a document object
6097 // Chrome and Firefox seem to allow this to occur and will throw exception
6098 // Fixes #8950
6099 if ( !doc.createDocumentFragment ) {
6100 doc = document;
6101 }
6102
第6086行:定义局部变量。变量fragment指向稍后可能创建的文档片段Document
Fragment;变量cacheable表示HTML代码是否符合缓存条件;变量cacheresults指向从缓存对象jQuery.fragments 中取到的文档片段,其中包含了缓存的DOM元素;变量doc表示创建文档片段的文档对象。
第6092~6101行:修正文档对象doc。数组nodes可能包含一个明确的文档对象,也可能包含jQuery对象或DOM元素,这里先尝试读取nodes[0]的属性ownerDocument并赋值给doc,ownerDocument表示DOM元素所在的文档对象。如果nodes[0].ownerDocument不存在,则假定nodes[0]为文档对象并赋值给doc;但doc依然可能不是文档对象,如果调用jQuery构造函数时第二个参数是JavaScript对象,此时doc是传入的JavaScript对象而不是文档对象,例如,执行“$('abc<div></div>',{'class':'test'} );”时,doc是{'class':'test'},此时需要检查doc.createDocumentFragment是否存在,如果不存在则修正doc为当前文档对象 document。
3.尝试从缓存对象jQuery.fragments中读取缓存的DOM元素
如果HTML代码符合缓存条件,则尝试从缓存对象jQuery.fragments中读取缓存的DOM元素,相关代码如下所示:
6103 // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
6104 // Cloning options loses the selected state, so don't cache them
6105 // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
6106 // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
6107 // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
6108 if ( args.length === 1 && typeof first === "string" &&
first.length < 512 &&
doc === document &&
6109 first.charAt(0) === "<" &&
!rnocache.test( first ) &&
6110 (jQuery.support.checkClone || !rchecked.test( first )) &&
6111 (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
6112
6113 cacheable = true;
6114
6115 cacheresults = jQuery.fragments[ first ];
6116 if ( cacheresults && cacheresults !== 1 ) {
6117 fragment = cacheresults;
6118 }
6119 }
6120
6133 jQuery.fragments = {};
第6108~6111:HTML代码必须满足以下所有条件,才认为符合缓存条件:
数组args的长度为1,且第一个元素是字符串,即数组args中只含有一段HTML代码。
HTML代码的长度小于512(1/2KB),否则可能会导致缓存占用的内存过大。
文档对象doc是当前文档对象,即只缓存为当前文档创建的DOM元素,不缓存其他框架(iframe)的。
HTML代码以左尖括号开头,即只缓存DOM元素,不缓存文本节点。
HTML代码中不能含有以下标签:<script>、<object>、<embed>、<option>、<style>。
当前浏览器可以正确地复制单选按钮和复选框的选中状态checked,或者HTML代码中的单选按钮和复选按钮没有被选中。
当前浏览器可以正确地复制HTML5元素,或者HTML代码中不含有HTML5标签。
HTML代码中不能含有的标签定义在正则rnocache中,该正则的定义代码如下所示:
5651 rnocache = /<(?:script|object|embed|option|style)/i,
HTML代码中的单选按钮和复选框是否被选中通过正则rchecked检测,该正则的定义代码如下所示:
5653 // checked="checked" or checked
5654 rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
HTML代码中是否含有HTML5标签通过正则rnoshimcache检测,该正则的定义代码如下所示:
5642 var nodeNames =
"abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|" +
5643 "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
5652 rnoshimcache = new RegExp(“<(?:” + nodeNames + “)”, “i”),
测试项jQuery.support.checkClone指示当前浏览器是否可以正确地复制单选按钮和复选按钮的选中状态checked,请参见7.2.14节,测试项jQuery.support.html5Clone指示当前浏览器是否可以正确地复制HTML5元素,请参见7.2.12节。
第6113行:如果HTML代码满足缓存条件,则设置变量cacheable为true。这是个很重要的状态值,在使用转换后的DOM元素时,如果变量cacheable为true,则必须先复制一份再使用,否则可以直接使用。
第6115~6118行:尝试从缓存对象jQuery.fragments中读取缓存的DOM元素。如果缓存命中,并且缓存值不是1,则表示读取到的是文档片段,赋值给变量fragment,文档片段中包含了缓存的DOM元素。稍后会看到对缓存对象jQuery.fragments中缓存值的分析。
4.?转换HTML代码为DOM元素
先创建一个文档片段DocumentFragment,然后调用方法jQuery.clean( elems, context, fragment, scripts )将HTML代码转换为DOM元素,并存储在创建的文档片段中。相关代码如下所示:
6121 if ( !fragment ) {
6122 fragment = doc.createDocumentFragment();
6123 jQuery.clean( args, doc, fragment, scripts );
6124 }
6125
第6121行:如果!fragment为true,表示需要执行转换过程。!fragment为true时可能有三种情况:
HTML代码不符合缓存条件。
HTML代码符合缓存条件,但此时是第一次转换,不存在对应的缓存。
HTML代码符合缓存条件,但此时是第二次转换,对应的缓存值是1。
第6122行:调用原生方法document.createDocumentFragment()创建文档片段。
第6123行:调用方法jQuery.clean()将HTML代码转换为DOM元素,并存储在创建的文档片段中。该方法将在2.5节介绍和分析。
5.?把转换后的DOM元素放入缓存对象jQuery.fragments
如果HTML代码符合缓存条件,则把转换后的DOM元素放入缓存对象jQuery.fragments中。相关代码如下所示:
6126 if ( cacheable ) {
6127 jQuery.fragments[ first ] = cacheresults ? fragment : 1;
6128 }
6129
第6126~6128行:如果HTML 代码符合缓存条件,则在缓存对象jQuery.fragments 中对应的缓存值如表2-2所示。
表2-2 符合缓存条件的HTML代码在缓存对象jQuery.fragments中对应的缓存值
场 景 转 换 前 转 换 后
第一次转换HTML代码 不存在 1
第二次转换同样的HTML代码 1 文档片段
两次以上转换同样的HTML代码 文档片段 文档片段
看到这里,可以把方法jQuery.buildFragment()的用法总结为:
如果HTML代码不符合缓存条件,则总是会执行转换过程。
如果HTML代码符合缓存条件,第一次转换后设置缓存值为1,第二次转换后设置为文档片段,从第三次开始则从缓存中读取。
6.?返回文档片段和缓存状态{ fragment: fragment, cacheable: cacheable }
相关代码如下所示:
6130 return { fragment: fragment, cacheable: cacheable };
6131 };
第6130行:最后会返回一个包含了文档片段fragment和缓存状态cacheable的对象。文档片段fragment中包含了转换后的DOM元素,缓存状态cacheable则指示了如何使用这些DOM元素。
2.4.3 小结
方法jQuery.buildFragment( args, nodes, scripts )的执行过程可以总结为如图2-5所示。