这个方法在jQuery源码中比较靠后的位置出现,主要用于两处。1是构造jQuery对象的时候使用 2.是为DOM操作提供底层支持,这也就是为什么先学习它的原因。之前的随笔已经分析过jQuery的构造函数了,也提到了有12个分支,其中有一个分支就是通过jQuery.buildFragment方法来处理的,什么情况呢?就是在处理复杂html标签的时候,例如$('<div>123</div>')这样的形式,在构造函数内部通过ret变量判断是不是简单标签,如果是就调用js的createElement方法直接创建元素如果不是呢就通过这个方法处理相关讲解可以参考字符串情况分类分析。本文主要是讨论此方法在构造jQuery对象上的作用。

了解文档片段documentFragment

Javascript中有documentFragment方法用于创建文档片段,什么是文档片段呢?就是创建出来的元素表示文档的一部分但是却不属于文档树。可以简单的理解为缓存的文档元素,通常我们会利用它来创建文档元素,我们可以利用文档片段的特性先把要插入的文档创建在文档片段中然后整体插入,相对于一个一个的插入文档元素而言性能会提高很多。这里引用一个例子:

//假如想创建十个段落,使用常规的方式可能会写出这样的代码:
var i = 0 ; i < 10; i ++) {
var p = document.createElement("p");
var oTxt = document.createTextNode("段落" + i);
p.appendChild(oTxt);
document.body.appendChild(p);
}
当然,这段代码运行是没有问题,但是他调用了十次document.body.appendChild(),每次都要产生一次页面渲染。这时碎片就十分有用了: var oFragment = document.createDocumentFragment(); for(var i = 0 ; i < 10; i ++) {
var p = document.createElement("p");
var oTxt = document.createTextNode("段落" + i);
p.appendChild(oTxt);
oFragment.appendChild(p);<br>}
document.body.appendChild(oFragment);
在这段代码中,每个新的<p />元素都被添加到文档碎片中,然后这个碎片被作为参数传递给appendChild()。这里对appendChild()的调用实际上并不是把文档碎片本省追加到body元素中,而是仅仅追加碎片中的子节点,然后可以看到明显的性能提升,document.body.appenChild()一次替代十次,这意味着只需要进行一个内容渲染刷新。

实现原理

jQuery.buildFragment方法会首先创建一个文档片段,然后在调用jQuery.clean方法将结果转换为dom元素,其中为了更好了提高效率,jQuery在此方法中加入了缓存机制,如果符合缓存会在读取时知己从缓存读取设置的时候也会有一个缓存备份供下一次使用。

源码分析

jQuery.buildFragment = function( args, nodes, scripts ) {
var fragment, cacheable, cacheresults, doc, first = args[ 0 ];
   ...
}

首先会接受3个参数args表示的待转换为DOM元素的HTML代码它是一个数组;nodes也是一个数组用于修正创建文档片段的的文档对象;script是存放script元素的这个主要跟dom操作有关系。这样看还是觉得没什么概念这些参数哪里来的?那么就找找之前调用方法的地方:

} else {
ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
}

在判断不是简单标签的时候就把[ match[1] ], [ doc ]这个两个东西传递了进来,现在知道为什么前两个参数都是数组了第一个数组是匹配到的标签第二个数组是文档对象也有可能是jquery对象或者dom元素不妨调用一下看看这些参数的值更清晰一点:

在html里面创建jquery对象

 <script>
$('<div><124/div>')
</script>

  然后再jQuery源码中查看参数

} else {
console.log(match[1]);
console.log(doc);
ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
}

在浏览器中运行的结果

<div><124/div>
#document<!DOCTYPE html><html>​…​</html>​

再换一个特殊一点的

  $('<div></div>',{'class':'test'});

在浏览器中运行的结果

<div><124/div>
jquery-1.7.1.js:157 Object {class: "test"}

这个时候的doc就是普通对象啦,至于第三个参数是在domManip方法中传递的是操作DOM的方法先不管。

紧接着声明了5个变量,fragment指向稍后可能创建的文档片段DocumentFragment;cacheable表示是否满足缓存条件,只有满足的才能进行缓存操作;cacheresults是从缓存对象读取的文档片段包含了缓存的DOM元素;变量doc表示创建文档片段的文档对象;first取得是数组的第一个元素因为也有可能创建多个元素比如$('<div>123</div>,<a>123</a>')。接着看源码:

// nodes may contain either an explicit document object,
// a jQuery collection or context object.
// If nodes[0] contains a valid object to assign to doc
if ( nodes && nodes[0] ) {
doc = nodes[0].ownerDocument || nodes[0];
}

这段代码是处理文档对象的,上面也分析过了传递过来的nodes里面可能是文档对象document也有可能是普通对象也有可能是jQuery对象或者DOM元素

如果nodes存在而且不是空的话先尝试获得它的ownerDocument把他赋值给doc,说白了就是先修正DOM元素的情况。

    // Ensure that an attr object doesn't incorrectly stand in as a document object
// Chrome and Firefox seem to allow this to occur and will throw exception
// Fixes #8950
if ( !doc.createDocumentFragment ) {
doc = document;
}

doc就是修正文档对象的,显然上一个方法不太给力只处理了DOM元素的情况,紧接着这一段就是处理不是DOM元素的情况,如果不存在createDocumentFragment的方法说明不是DOM元素就直接让doc等于document,这里doc修正完毕会始终是创建文档片段的文档对象。

    // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
// Cloning options loses the selected state, so don't cache them
// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
// Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
first.charAt(0) === "<" && !rnocache.test( first ) &&
(jQuery.support.checkClone || !rchecked.test( first )) &&
(jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
cacheable = true;
cacheresults = jQuery.fragments[ first ];
if ( cacheresults && cacheresults !== 1 ) {
fragment = cacheresults;
}
}

这一段是处理缓存的的,首先要判断一下是不是符合缓存条件其中包括了

1、数组 args 的长度为 1,且第一个元素是字符串,即数组 args 中只含有一段 HTML代码。
‰ 2、 HTML 代码的长度小于 512(1/2KB) ,否则可能会导致缓存占用的内存过大。
 3、‰ ‰ 文档对象 doc 是当前文档对象,即只缓存为当前文档创建的 DOM 元素,不缓存其他框架(iframe)的。
 4、‰ ‰ HTML 代码以左尖括号开头,即只缓存 DOM 元素,不缓存文本节点。
‰ 5、 HTML 代码中不能含有以下标签:<script>、<object>、<embed>、<option>、<style>。
‰ 6、 当前浏览器可以正确地复制单选按钮和复选框的选中状态 checked,或者 HTML 代码中的单选按钮和复选按钮没有被选中。
 7、‰ ‰ 当前浏览器可以正确地复制 HTML5 元素,或者 HTML 代码中不含有 HTML5 标签

后面四个条件用到了jquery的功能检测和正则主要是判断这些条件,一旦这些条件满足就把cacheable属性改成true表示符合缓存条件。

然后尝试着从jQuery.fragments对象中读取缓存结果,如果之前已经创建过dom元素了在jQuery.fragments对象中有结果那就直接调缓存结果jQuery.fragments 方法在jQuery.buildFragment方法之后声明用于保存缓存的文档片段默认为空。

如果cacheresults有值而且不为1就调缓存结果,把结果赋值给fragment,为什么要判断为1呢?看到后面就明白了。

jQuery.fragments = {};

接着看源码:

    if ( !fragment ) {
fragment = doc.createDocumentFragment();
jQuery.clean( args, doc, fragment, scripts );
}
fragment没有值说明没有缓存结果那就自己创建文档片段交给fragment然后调用clean方法转成dom元素。其实最终是clean方法处理的,找个方法比较复杂下一篇再分析吧。
    if ( cacheable ) {
jQuery.fragments[ first ] = cacheresults ? fragment : 1;
} return { fragment: fragment, cacheable: cacheable };

接着往后看,如果符合缓存条件就把结果缓存起来保存到jQuery.fragments这个对象中去,它的结果取决于cacheresults是否存在,不存在就是1这就是为什么前面要判断cacheresults !== 1的原因。最终返回一个对象包含文档片段和是否满足缓存条件的结果其他地方再根据返回的结果进行相应的处理。

现在再来完整的看一下这个方法的用法:首先当假设我们第一次传入一个复杂标签创建jQuery对象时会首先修正文档对象保证为document,然后会判断是否满足缓存条件,然后找到存储缓存文档片段的对象jQuery.fragments看是否有值把结果就赋值给cacheresults,cacheresults有值在把他赋值给fragment,由于是第一次使用肯定是没有值得f ( !fragment )是为真的这个时候调用js方法fragment = doc.createDocumentFragment();创建文档片段fragment,最后用clean方法处理文档片段构造jQuery对象。假设我们传递的参数是支持缓存的那么cacheable为真但是cacheresults又不存在所以jQuery.fragments[ first ]值变成了一。当我们第二次在使用相同的参数时,由于不满足cacheresults !== 1的条件所以依然会自己创建但是此时的cacheresults是1并非没有值这个时候才把创建好的文档片段fragment放到jQuery.fragments中,当我们第三次使用同样的参数时就会自动调用jQuery.fragments的结果啦,所以是第三次开始才能使用缓存的。为什么jQuery要在第三次才能调用缓存呢?完全可以在第一次就把缓存结果保存在jQuery.fragments去第二次就可以调用了,这里可能有其他的用处我暂时还没有想好。最后附上完整源码:

 Query.buildFragment = function( args, nodes, scripts ) {
var fragment, cacheable, cacheresults, doc,
first = args[ 0 ]; // nodes may contain either an explicit document object,
// a jQuery collection or context object.
// If nodes[0] contains a valid object to assign to doc
if ( nodes && nodes[0] ) {
doc = nodes[0].ownerDocument || nodes[0];
} // Ensure that an attr object doesn't incorrectly stand in as a document object
// Chrome and Firefox seem to allow this to occur and will throw exception
// Fixes #8950
if ( !doc.createDocumentFragment ) {
doc = document;
} // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
// Cloning options loses the selected state, so don't cache them
// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
// Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
first.charAt(0) === "<" && !rnocache.test( first ) &&
(jQuery.support.checkClone || !rchecked.test( first )) &&
(jQuery.support.html5Clone || !rnoshimcache.test( first )) ) { cacheable = true; cacheresults = jQuery.fragments[ first ];
if ( cacheresults && cacheresults !== 1 ) {
fragment = cacheresults;
}
} if ( !fragment ) {
fragment = doc.createDocumentFragment();
jQuery.clean( args, doc, fragment, scripts );
} if ( cacheable ) {
jQuery.fragments[ first ] = cacheresults ? fragment : 1;
} return { fragment: fragment, cacheable: cacheable };
}; jQuery.fragments = {};
												

jQuery.buildFragment源码分析以及在构造jQuery对象的作用的更多相关文章

  1. jQuery.attributes源码分析(attr/prop/val/class)

    回顾 有了之前的几篇对于jQuery.attributes相关的研究,是时候分析jQuery.attr的源码了 Javascript中的attribute和property分析 attribute和p ...

  2. jQuery.queue源码分析

    作者:禅楼望月(http://www.cnblogs.com/yaoyinglong ) 队列是一种特殊的线性表,它的特殊之处在于他只允许在头部进行删除,在尾部进行插入.常用来表示先进先出的操作(FI ...

  3. jQuery.access源码分析

    基本理解 jQuery.attr是jQuery.attr,jQuery.prop,jQuery.css提供底层支持,jQuery里一个比较有特色的地方就是函数的重载, 比如attr,有如下几种重载 $ ...

  4. Python源码分析(二) - List对象

    python中的高级特性之一就是内置了list,dict等.今天就先围绕列表(List)进行源码分析. Python中的List对象(PyListObject) Python中的的PyListObje ...

  5. jQuery.Deferred 源码分析

    作者:禅楼望月(http://www.cnblogs.com/yaoyinglong ) 1 引子 观察者模式是我们日常开发中经常用的模式.这个模式由两个主要部分组成:发布者和观察者.通过观察者模式, ...

  6. mybatis源码分析(四)---------------代理对象的生成

    在mybatis两种开发方式这边文章中,我们提到了Mapper动态代理开发这种方式,现在抛出一个问题:通过sqlSession.getMapper(XXXMapper.class)来获取代理对象的过程 ...

  7. dubbo源码分析4——SPI机制_ExtensionFactory类的作用

    ExtensionFactory的源码: @SPI public interface ExtensionFactory { /** * Get extension. * * @param type o ...

  8. 序列化与反序列化、def的介绍与快速使用、cbv源码分析、APIView与request对象分析

    今日内容概要 序列化与反序列化 def介绍和快速使用 cbv源码流程分析 drf之APIView和Request对象分析 内容详细 1.序列化和反序列化 # api接口开发 最核心最常见的一个过程就是 ...

  9. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

随机推荐

  1. 数据可视化(1)--Chart.js

    Chart.js是一个HTML5图表库,使用canvas元素来展示各式各样的客户端图表,支持折线图.柱形图.雷达图.饼图.环形图等.在每种图表中,还包含了大量的自定义选项,包括动画展示形式. Char ...

  2. Quartz Java resuming a job excecutes it many times--转

    原文地址:http://stackoverflow.com/questions/1933676/quartz-java-resuming-a-job-excecutes-it-many-times Q ...

  3. sublime 插件总结

    sublime的强大之处在于其丰富的插件,记录一下常用的插件. 1.Color Highlighter(识别代码中的颜色) 默认如下显示 做如下修改,打开插件默认设置,并复制到用户设置,将 " ...

  4. 设置Android程序图标

    先在/res/drawable/目录下放一个叫icon.png的图标图片(48×48),并且在/res/values/strings.xml中定义app_name 修改<string name= ...

  5. Elasticsearch聚合 之 Range区间聚合

    Elasticsearch提供了多种聚合方式,能帮助用户快速的进行信息统计与分类,本篇主要讲解下如何使用Range区间聚合. 最简单的例子,想要统计一个班级考试60分以下.60到80分.80到100分 ...

  6. JSP中<img>标签引用本地图片

    问题描述: jsp页面中<img>标签如何读取本地文件夹中的图片. 问题起因: 由于上传图片至本地文件夹中,图片路径为: D:/upload/file/image/img.jpg 所以将这 ...

  7. 图论 ---- spfa + 链式向前星 ---- poj 3268 : Silver Cow Party

    Silver Cow Party Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 12674   Accepted: 5651 ...

  8. PHP类和对象等代码说明

    1.定义和创建类和对象: 定义类要使用class关键字.例如:class 类名{//属性和方法} 创建对象使用new关键字.例如: $p1 = new 类名;,可以基于一个类创建多个对象. 2. 类属 ...

  9. C# winform OpenFileDialog MessageBox

    1.弹出窗体选择本地文件-OpenFileDialog OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Tit ...

  10. 15天玩转redis —— 第六篇 有序集合类型

    今天我们说一下Redis中最后一个数据类型 “有序集合类型”,回首之前学过的几个数据结构,不知道你会不会由衷感叹,开源的世界真好,写这 些代码的好心人真的要一生平安哈,不管我们想没想的到的东西,在这个 ...