各种动态渲染Element方式的性能探究

【引自雕刻零碎的博客】一、性能优化的原则及方法论

树立原则:动态渲染进入一个Dom元素,首先需要保证动态渲染操作必须尽可能少对原有dom树的影响,影响重绘及重排。

确定方法论:必须寻找一个容器来缓存渲染期间生成的dom结构(操作必须尽可能少对原有dom树的影响),然后再进行一次渲染到目标element中。

二、生成期间DOM缓存的选择

  • DocumentFragment(文档碎片对象,选择原因:脱离于文档流)
  • 临时Element(选择原因:新element脱离于文档流)
    • createElement,再一步步进行渲染
    • 通过描述Dom的String(下称:DomString),转化为Dom对象
      • 临时Element+innerHTML+cloneNode返回最外层element元素对象,再进行插入appendChild,必要时还需要选择器方法讲某一个Element对象提取出来
      • XML字符串通过解析生成Element对象(注意,不是HTMLxxxElement对象,是Element对象),然后将该对象appendChild进去
  • 临时字符串(选择原因:借助innerHTML渲染,一次渲染)

三、DocumentFragment的优缺点

基本模式:

 
 
 
  1. var fragment = document.createDocumentFragment(); 
  2.     fragment.appendChild( 
  3.         ...    //生成Element的IIFE 
  4.     )  
 
 
 
  1. //IIFE示例,根据配置创建元素 
  2. var ConfigVar = { 
  3.   ELname:"div", 
  4.   id:"blablabla", 
  5.   name:"balblabla", 
  6.   class:"ClassName" 
  7. (function(Config){ 
  8.       var el = document.createElement(Config.ELname); 
  9.           el.className = (Config.class || ""); 
  10.     for (let AttrName in Config){ 
  11.           if (AttrName == "class")continue; 
  12.           el.setAttribute(AttrName,Config[AttrName]); 
  13.     } 
  14.       return el; 
  15. })(ConfigVar)  

优点

1、脱离于文档流,操作不会对Dom树产生影响

2、在每一次生成临时Element时候就可以将该Element对象的引用保存下来,而不需要多次用选择器再次获取。

缺点

兼容性只是达到IE9+

http://caniuse.com/#search=DocumentFragment

四、createElement的优缺点

基本模式

 
 
 
  1. var el = document.createElement("ElementName");     
  2.     el.className = ""; 
  3.     el.setAttribute("AttrName",AttrValue); 
  4.     el.setAttribute("AttrName",AttrValue); 
  5.     ... 
  6.     el.appendChild(         
  7.           ... //生成Element的IIFE,见上文 
  8.     );  

优点

1、新创建的元素脱离于文档流,操作不会对Dom树产生影响

2、兼容性***

3、在每一次生成临时Element时候就可以将该Element对象的引用保存下来,而不需要多次用选择器再次获取。

缺点

每一次调用setAttribute方法都是一次次对Element进行修改,此处具有潜在的性能损耗。

五、DomString——临时Element+innerHTML+cloneNode的优缺点

基本模式

 
 
 
  1. var domString2Dom = (function(){ 
  2.     if (window.HTMLTemplateElement){ 
  3.         var container = document.createElement("template"); 
  4.         return function(domString){ 
  5.             container.innerHTML = domString; 
  6.             return container.content.firstChild.cloneNode(true) 
  7.         } 
  8.     }else{ 
  9.         //对不支持的template 的浏览器还有兼容性方法没写,所以不支持tr,td等些元素inner进div中。 
  10.         var container = document.createElement("div"); 
  11.         return function(domString){ 
  12.             container.innerHTML = domString; 
  13.             return container.firstChild.cloneNode(true) 
  14.         }         
  15.     } 
  16. })();  
 
 
 
  1. var template = domString2Dom('
'); 
  • for (var index = 0; index < 80; index++) {     
  •   template.appendChild( 
  •     (function(){ 
  •       var el = domString2Dom("
    M
    "); 
  •       return el 
  •     })() 
  •   )                 
  • }  
  • 优点

    创建Dom之后不需要多次进行setAttribute

    缺点

    1、临时元素不能包裹一些特定的元素(不能在所有浏览器的所有 HTML 元素上设置 innerHTML 属性)

    2、解析的过程进行了很多其余的操作。此处具有潜在的性能损耗。

    3、插入的字符串***层Node只允许有一个元素

    六、DomString——XML解析的优缺点

    基本模式

     
     
     
    1. var XMLParser = function () { 
    2.     var $DOMParser = new DOMParser(); 
    3.     return function (domString) { 
    4.         if (domString[0] == "<") { 
    5.             var doc = $DOMParser.parseFromString(domString, "application/xhtml+xml"); 
    6.             return doc.firstChild; 
    7.         } 
    8.         else { 
    9.             return document.createTextNode(domString); 
    10.         } 
    11.     }; 
    12. }();  
     
     
     
    1. var template = XMLParser('
    '); 
  • for (var index = 0; index < 80; index++) { 
  •   template.appendChild((function () { 
  •     var el = XMLParser("
    M
    "); 
  •     return el; 
  •   })()); 
  • }  
  • 优点

    DomString方法中通用性***的,虽然IE10+才支持DOMParser,但是IE9以下的有替代方法

    缺点

    1、解析的过程本身就具有潜在的性能损耗。

    2、只能得到刚刚创建最外层元素的克隆。子元素的引用还需要用选择器。

    3、插入的字符串***层Node只允许有一个元素

    七、临时字符串的优缺点

    基本模式:

     
     
     
    1. var template = document.createElement("div"); 
    2. template.innerHTML = ` 
    3.                         Test TextNode 
    4.                         ${(function(){ 
    5.                           var temp = new Array(8); 
    6.                           for (var index = 0; index < 80; index++) { 
    7.                             temp[index]="
      M
    8.                           } 
    9.                           return temp.join() 
    10.                         }())} 
    11.                       
    ` //需要增加的一大段Element  

    优点

    1、通用性***,不需要逐步创建一大堆无用的Element对象引用

    2、运用es6模板字符串编码优雅,不需要字符串用加号进行链接

    缺点

    1、如果是直接给出配置Config进行渲染需要进行字符串的生成

    2、只能得到刚刚创建最外层元素的引用。子元素的引用还需要用选择器。

    八、Template元素

    由于HTML5中新增了template元素

    其特点就是有一个content属性是HTMLDocumentFragment对象,所以可以包容任何元素

    基本范式是:

     
     
     
    1. var template = document.createElement("template"); 
    2. template.innerHTML = ` 
    3.                         Test TextNode 
    4.                         ${(function(){ 
    5.                           var temp = new Array(8); 
    6.                           for (var index = 0; index < 80; index++) { 
    7.                             temp[index]="
      M
    8.                           } 
    9.                           return temp.join() 
    10.                         }())} 
    11.                       ` //需要增加的一大段Element 
    12. // template.content 是HTMLDocumentFragment  

    优点

    比div要好很多,作为临时元素容器的包容性更强

    缺点

    兼容性不好:http://caniuse.com/#search=HTML%20templates 在不支持的浏览器中表示为HTMLUnknownElement

    九、各种方法的效率对比

    测试代码:(由于笔者不太熟悉各种浏览器性能的BUG,这里的代码如果有不足请指正),代码由typescript进行编写,也可以用babel进行编译。

     
     
     
    1. /** 
    2.  * @param Count:渲染DOM结构的次数 
    3.  */ 
    4. var DateCount = { 
    5.     TimeList : {}, 
    6.     time:function(Str){ 
    7.         console.time(Str); 
    8.     }, 
    9.     timeEnd:function(Str){ 
    10.         console.timeEnd(Str); 
    11.     } 
    12. }; 
    13. //==================工具函数====================== 
    14. var domString2Dom = (function () { 
    15.     var container; 
    16.     if (window.HTMLTemplateElement) { 
    17.         container = document.createElement("template"); 
    18.         return function (domString) { 
    19.             container.innerHTML = domString; 
    20.             return container.content.firstChild.cloneNode(true); 
    21.         }; 
    22.     } 
    23.     else { 
    24.         //对不支持的template 的浏览器还有兼容性方法没写,所以不支持tr,td等些元素inner进div中。 
    25.         container = document.createElement("div"); 
    26.         return function (domString) { 
    27.             container.innerHTML = domString; 
    28.             return container.firstChild.cloneNode(true); 
    29.         }; 
    30.     } 
    31. })(); 
    32. var XMLParser = (function () { 
    33.     var $DOMParser; 
    34.     if (window.DOMParser) { 
    35.         $DOMParser = new DOMParser(); 
    36.         return function (domString) { 
    37.             if (domString[0] == "<") { 
    38.                 var doc = $DOMParser.parseFromString(domString, "application/xhtml+xml"); 
    39.                 return doc.firstChild; 
    40.             } 
    41.             else { 
    42.                 return document.createTextNode(domString); 
    43.             } 
    44.         }; 
    45.     }else{ 
    46.         $DOMParser = new ActiveXObject("Microsoft.XMLDOM"); 
    47.         return function (domString) { 
    48.             if (domString[0] == "<") { 
    49.                 $DOMParser.async = false; 
    50.                 $DOMParser.loadXML(domString);    
    51.                 return $DOMParser 
    52.             } 
    53.             else { 
    54.                 return document.createTextNode(domString); 
    55.             }                 
    56.         } 
    57.  
    58.     } 
    59.  
    60. })(); 
    61. //=============================================== 
    62.  
    63. var Test = function(Count){ 
    64.     //保留这种写法,能够在移动端平台中不依靠控制台进行效率测试 
    65.     // var DateCount = { 
    66.     //     TimeList : {}, 
    67.     //     time:function(Str){ 
    68.     //         this.TimeList[Str] = Date.now(); 
    69.     //     }, 
    70.     //     timeEnd:function(Str){ 
    71.     //         alert(Str+(Date.now() - this.TimeList[Str])); 
    72.     //     } 
    73.     // } 
    74.  
    75.         //基准测试1: 
    76.     DateCount.time("无临时div + 不需要字符串拼接 + innerHTML:") 
    77.     for (let index = 0; index < Count; index++) { 
    78.         (function(){ 
    79.             var template = document.createElement("div"); 
    80.                 template.className = "TestClass"; 
    81.                 template.setAttribute("Arg","TestArg") 
    82.                 template.innerHTML = ` Test TextNode 
    83. MMMMMMMMM
    84. MMMMMMMMM
    85. MMMMMMMMM
    86. MMMMMMMMM
    87. MMMMMMMMM
    88. MMMMMMMMM
    89. MMMMMMMMM
    90. MMMMMMMMM
    91. MMMMMMMMM
    92. MMMMMMMMM
    93. MMMMMMMMM
    94. MMMMMMMMM
    95. ` //需要增加的一大段Element,共100个子级div 
    96.             return template 
    97.         }())   
    98.     } 
    99.     DateCount.timeEnd("无临时div + 不需要字符串拼接 + innerHTML:") 
    100.  
    101.     //基准测试2: 
    102.     DateCount.time("createElement+appendChild写法:") 
    103.     for (let index = 0; index < Count; index++) { 
    104.         (function(){ 
    105.             var template = document.createElement("div"); 
    106.                 template.className = "TestClass"; 
    107.                 template.setAttribute("Arg","TestArg") 
    108.  
    109.                 template.appendChild(document.createTextNode('Test TextNode')); 
    110.                 for (let index = 0; index < 100; index++) { 
    111.                     let element = document.createElement("div"); 
    112.                         element.setAttribute("child","true"); 
    113.                         element.appendChild(document.createTextNode("M")) 
    114.                         template.appendChild(element) 
    115.                 } 
    116.             return template 
    117.         }())   
    118.     } 
    119.     DateCount.timeEnd("createElement+appendChild写法:")     
    120.  
    121.     //DocumentFragment  
    122.     DateCount.time("DocumentFragment+ createElement+appendChild 写法:") 
    123.     for (let index = 0; index < Count; index++) { 
    124.         (function(){ 
    125.             var fragment = document.createDocumentFragment(); 
    126.                 fragment.appendChild(function(){ 
    127.                     var template = document.createElement("div"); 
    128.                         template.className = "TestClass"; 
    129.                         template.setAttribute("Arg","TestArg") 
    130.  
    131.                         template.appendChild(document.createTextNode('Test TextNode')); 
    132.                         for (let index = 0; index < 100; index++) { 
    133.                             let element = document.createElement("div"); 
    134.                                 element.setAttribute("child","true"); 
    135.                                 template.appendChild(element) 
    136.                         } 
    137.                     return template; 
    138.                 }()); 
    139.  
    140.             return fragment 
    141.         }())   
    142.     } 
    143.     DateCount.timeEnd("DocumentFragment+ createElement+appendChild 写法:")     
    144.  
    145.     //DomString——临时Element+innerHTML+cloneNode 
    146.     // DateCount.time("DomString——临时Element+innerHTML+cloneNode:") 
    147.     // for (let index = 0; index < Count; index++) { 
    148.     //     (function(){ 
    149.     //         var template = domString2Dom(''); 
    150.     //         for (let index = 0; index < 100; index++) {     
    151.     //             template.appendChild( 
    152.     //                 (function(){ 
    153.     //                     var el = domString2Dom("M"); 
    154.     //                     return el 
    155.     //                 })() 
    156.     //             )                 
    157.     //         } 
    158.     //         return template; 
    159.     //     }())   
    160.     // } 
    161.     // DateCount.timeEnd("DomString——临时Element+innerHTML+cloneNode:")     
    162.  
    163.     //DomString——XML解析 
    164.     // DateCount.time("DomString——XML解析:") 
    165.     // for (let index = 0; index < Count; index++) { 
    166.     //     (function(){ 
    167.     //         var template = XMLParser(''); 
    168.     //         for (let index = 0; index < 100; index++) { 
    169.     //             template.appendChild((function () { 
    170.     //                 var el = XMLParser("M"); 
    171.     //                 return el; 
    172.     //             })()); 
    173.     //         } 
    174.     //     }())   
    175.     // } 
    176.     // DateCount.timeEnd("DomString——XML解析:")    
    177.  
    178.     //临时div + 临时字符串拼接: 
    179.     DateCount.time("临时div + 字符串拼接:") 
    180.     for (let index = 0; index < Count; index++) { 
    181.         (function(){ 
    182.             let template = document.createElement("div"); 
    183.             template.innerHTML = ` 
    184.                                     Test TextNode 
    185.                                     ${(function(){ 
    186.                                         let temp = ""; 
    187.                                         for (let index = 0; index < 100; index++) { 
    188.                                             temp+="M" 
    189.                                         } 
    190.                                         return temp 
    191.                                     }())} 
    192.                                 ` //需要增加的一大段Element 
    193.             return template.firstChild; 
    194.          }())   
    195.     } 
    196.     DateCount.timeEnd("临时div + 字符串拼接:") 
    197.  
    198.     //临时template + 临时字符串拼接: 
    199.     DateCount.time("临时template + 字符串拼接:") 
    200.     for (let index = 0; index < Count; index++) { 
    201.         (function(){ 
    202.             var template = document.createElement("template"); 
    203.             template.innerHTML = ` 
    204.                                     Test TextNode 
    205.                                     ${(function(){ 
    206.                                         let temp = ""; 
    207.                                         for (let index = 0; index < 100; index++) { 
    208.                                             temp+="M" 
    209.                                         } 
    210.                                         return temp 
    211.                                     }())} 
    212.                                 ` //需要增加的一大段Element 
    213.             return template.content; 
    214.          }())   
    215.     } 
    216.     DateCount.timeEnd("临时template + 字符串拼接:") 
    217.  
    218.     //临时template + createElement+appendChild 写法 
    219.     DateCount.time("template + createElement+appendChild 写法:") 
    220.     for (let index = 0; index < Count; index++) { 
    221.         (function(){ 
    222.             var template = document.createElement("template"); 
    223.                 template.appendChild(function(){ 
    224.                     var template = document.createElement("div"); 
    225.                         template.className = "TestClass"; 
    226.                         template.setAttribute("Arg","TestArg") 
    227.  
    228.                         template.appendChild(document.createTextNode('Test TextNode')); 
    229.                         for (let index = 0; index < 100; index++) { 
    230.                             let element = document.createElement("div"); 
    231.                                 element.setAttribute("child","true"); 
    232.                                 template.appendChild(element) 
    233.                         } 
    234.                     retur

      网站标题:各种动态渲染Element方式的性能探究
      文章转载:http://www.hantingmc.com/qtweb/news22/474772.html

      网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

      广告

      声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联