<b>2.4 jquery.buildfragment(</b>
args, nodes, scripts )
<b>2.4.1 實作原理</b>
方法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章介紹和分析。
<b>2.4.2 源碼分析</b>
方法jquery.buildfragment( args, nodes, scripts )執行的5個關鍵步驟如下:
1)如果html代碼符合緩存條件,則嘗試從緩存對象jquery.fragments中讀取緩存的dom元素。
2)建立文檔片段documentfragment。
3)調用方法jquery.clean(
4)如果html代碼符合緩存條件,則把轉換後的dom元素放入緩存對象jquery.fragments。
5)最後傳回文檔片段和緩存狀态{
fragment: fragment, cacheable: cacheable }。
下面來看看該方法的源碼實作。
1.?定義jquery.buildfragment(
相關代碼如下所示:
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元素。
<b>2.4.3 小結</b>
方法jquery.buildfragment( args, nodes, scripts )的執行過程可以總結為如圖2-5所示。