[原創] jQuery源碼分析-12 DOM操作-Manipulation-核心函數jQuery.buildFragment()


作者:nuysoft/高雲 QQ:47214707 Email:nuysoft@gmail.com
聲明:本文為原創文章,如需轉載,請注明來源並保留原文鏈接。

jQuery源碼分析系列(持續更新)

 

基於 jQuery 1.7.1 編寫

 

核心函數 jQuery.buildFragment()

 

概述

關於DocumentFragment的討論

函數定義

修正Document對象doc

是否符合緩存條件

創建文檔碎片,轉換HTML代碼

如果符合緩存條件,將文檔碎片放入緩存

返回文檔碎片和緩存狀態

 

概述

 

DOM操作的三個核心函數的調用鏈依次是:

.domManip( args, table, callback ) > jQuery.buildFragment( args, nodes, scripts ) > jQuery.clean( elems, context, fragment, scripts )

clip_image002

jQuery.buildFragment()接受.domManip()傳入的HTML代碼,創建一個文檔碎片DocumentFragment,然后調用jQuery.clean()在這個文檔碎片上將HTML代碼轉換為DOM元素,jQuery.buildFragment()的實現有2個亮點值得借鑒:

1. 如果要插入多個DOM元素,可以先將這些DOM元素插入一個文檔碎片,然后將文檔碎片插入文檔中,這時插入的不是文檔碎片,而是它的子孫節點;相比於挨個插入DOM元素,使用文檔碎片可以獲得2-3倍的性能提升;

2. 如果將重復的HTML代碼轉換為DOM元素,可以將轉換后的DOM元素緩存起來,下次(實際是第3次)轉換同樣的HTML代碼時,可以直接緩存的DOM元素克隆返回

這些技巧在后邊的源碼分析中會詳細描述,不過在開始之前,看看關於文檔碎片的資料和討論,可以幫助我們接下來更好的學習源碼。

 

關於DocumentFragment的討論

 

1. John Resig 2008年的一篇文章 http://ejohn.org/blog/dom-documentfragments/

   demo: http://ejohn.org/apps/fragment/

   測試結果:

Browser

Normal(ms)

Fragment(ms)

Firefox 3.0.1

90

47

Safari 3.1.2

156

44

Opera 9.51

208

95

IE 6

401

140

IE 7

230

61

IE 8b1

120

40

   A method that is largely ignored in modern web development can provide some serious (2-3x) performance improvements to your DOM manipulation.

2. http://msdn.microsoft.com/zh-cn/library/system.xml.xmldocument.createdocumentfragment(pt-br,VS.80).aspx

   不能將 DocumentFragment 節點插入文檔中。但是,可以將 DocumentFragment 節點的子集插入文檔中。

3. http://www.w3school.com.cn/xmldom/dom_documentfragment.asp

   XML DOM - DocumentFragment 對象

   DocumentFragment 對象表示鄰接節點和它們的子樹。

   DocumentFragment 接口表示文檔的一部分(或一段)。更確切地說,它表示一個或多個鄰接的 Document 節點和它們的所有子孫節點。

   DocumentFragment 節點不屬於文檔樹,繼承的 parentNode 屬性總是 null。

   不過它有一種特殊的行為,該行為使得它非常有用,即當請求把一個 DocumentFragment 節點插入文檔樹時,插入的不是 DocumentFragment 自身,而是它的所有子孫節點。這使得 DocumentFragment 成了有用的占位符,暫時存放那些一次插入文檔的節點。它還有利於實現文檔的剪切、復制和粘貼操作,尤其是與 Range 接口一起使用時更是如此。

   可以用 Document.createDocumentFragment() 方法創建新的空 DocumentFragment 節點。

   也可以用 Range.extractContents() 方法 或 Range.cloneContents() 方法 獲取包含現有文檔的片段的 DocumentFragment 節點。

4. http://www.w3.org/TR/2002/WD-DOM-Level-3-Core-20020409/core.html

   When a DocumentFragment is inserted into a Document (or indeed any other Node that may take children) the children of the DocumentFragment and not the DocumentFragment itself are inserted into the Node.

   This makes the DocumentFragment very useful when the user wishes to create nodes that are siblings; the DocumentFragment acts as the parent of these nodes so that the user can use the standard methods from the Node interface, such as insertBefore and appendChild.

 

函數定義

 

   1:      jQuery.buildFragment = function( args, nodes, scripts ) {

 

args 待插入的DOM元素或HTML代碼

nodes jQuery對象,從其中獲取Document對象,doc = nodes[0].ownerDocument || nodes[0]

scripts 腳本數組,依次傳遞:.domManip() > jQuery.buildFragment() > jQuery.clean()

 

修正Document對象doc

 

   2:          var fragment, cacheable, cacheresults, doc,
   3:          first = args[ 0 ];
   4:      
   5:          // nodes may contain either an explicit document object,
   6:          // a jQuery collection or context object.
   7:          // If nodes[0] contains a valid object to assign to doc
   8:          if ( nodes && nodes[0] ) {
   9:              doc = nodes[0].ownerDocument || nodes[0];
   10:          }
  11:      
  12:          // Ensure that an attr object doesn't incorrectly stand in as a document object
  13:          // Chrome and Firefox seem to allow this to occur and will throw exception
  14:          // Fixes #8950
  15:          if ( !doc.createDocumentFragment ) {
  16:              doc = document;
  17:          }
  18:      

 

修正doc對象,后邊要在doc上調用createDocumentFragment

 

是否符合緩存條件

 

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

 

先翻譯源碼注釋:

只在主document中緩存1/2KB的HTML(length < 512);

復制options會丟失選中狀態,不處理;

在IE6中,如果在fragment中添加<object> <embed>,不能正常工作,不處理;

在WebKit中,復制節點時無法復制checked屬性,不處理;

最后,IE6/7/8不能正確的復用緩存的文檔碎片,如果碎片是通過未知元素創建的。

 

看看if塊需要滿足的條件(真是苛刻啊):

args長度等於1 + 第一個元素是字符串 + 字符串長度小於512B + 主文檔對象

+ 第一個字符是左尖括號 + 支持緩存的標簽(除了/<(?:script|object|embed|option|style)/i)

+ 要么支持正確的checked屬性賦值 要么沒有checked屬性

+ 要么可以正確拷貝HTML5元素 要么不是HTML5標簽(在nodeNames中定義)

第29行:cacheable表示first是否滿足緩存條件可放入緩存;

第31行:從緩存jQuery.fragments中查找緩存結果,jQuery.fragments是全局的文檔碎片緩存對象

第32行:緩存命中,並且不是1,而是真正的文檔碎片,1是第一次調用jQuery.buildFragment時設置的,接着往下看

 

創建文檔碎片,轉換HTML代碼

 

   37:          if ( !fragment ) {
   38:              fragment = doc.createDocumentFragment();
   39:              jQuery.clean( args, doc, fragment, scripts );
   40:          }
   41:      

 

如果!fragment 為true,創建一個文檔碎片,在該文檔碎片上調用jQuery.clean將HTML字符串args轉換為DOM元素

!fragment 為true,可能的情況:

1. 不符合緩存條件

2. 符合緩存條件 第一次調用jQuery.buildFragment,此時緩存中還沒有

3. 符合緩存條件 第二次調用jQuery.buildFragment,此時緩存中是1

 

如果符合緩存條件,將文檔碎片放入緩存

 

   42:          if ( cacheable ) {
   43:              jQuery.fragments[ first ] = cacheresults ? fragment : 1;
   44:          }
   45:      

 

如果符合緩存條件,那么放入緩存,放入的可能是文檔碎片fragment或1,取決於緩存中的已有值;如果已有值是undefiend則為1,如果是1則為fragment。

看到這里,jQuery.buildFragment的使用可以總結為(我們先只考慮滿足緩存條件的情況):

第一次 置為1

第二次 置為fragment

第三次 開始享受緩存帶來的性能提升

 

返回文檔碎片和緩存狀態

 

   46:          return { fragment: fragment, cacheable: cacheable };
   47:      };

 

返回Map結構的對象, cacheable 是一個重要的屬性,滿足緩存條件的fragment在使用時只能克隆,不能用原始的fragment


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM