本文介绍了“各种动态绘制元素方法的性能分析”的知识。很多人在实际案例操作中都会遇到这样的困难。接下来,让边肖带领大家学习如何应对这些情况!希望大家认真阅读,学点东西!
一、性能优化的原则及方法论
原理:在动态渲染进入一个dom元素之前,首先要保证动态渲染操作对原Dom树的影响必须尽可能小,影响重绘和重排。
确定方法:我们必须找到一个容器来缓存渲染过程中生成的dom结构(操作必须对原始dom树影响尽可能小),然后再次将其渲染到目标元素。
二、生成期间DOM缓存的选择
文档片段(文档片段对象,选择原因:与文档流分离)
临时元素(选择原因:新元素与文档流分离)
临时元素内部HTML克隆代码返回最外面的元素对象,然后插入appendChild。如果需要,需要一个选择器方法来提取元素对象。
解析该XML字符串以生成一个Element对象(注意,它是一个Element对象,而不是一个HTMLxxxElement对象),然后该对象是appendChild。
创建元素,然后逐步渲染。
通过描述Dom的字符串(以下称为DomString),它被转换成一个Dom对象。
临时字符串(选择原因:用innerHTML呈现,一次呈现)
三、DocumentFragment的优缺点
基本模式:
var fragment=document . createdocumentfragment();Fragment.appendChild(.//life生成Element)//life示例,根据配置,创建元素varconfigvar={El name :' div ',ID 3360' blablabla ',name 3360' balbla ',class: ' ClassName(函数(Config){ varel=document . createelement(Config。ELname);El . class name=(Config . class | | ' ');for(letattrnameconfig){ if(AttrName=' class ')继续;el.setAttribute(AttrName,Config[AttrName]);} returnel})(配置变量)优势
1.与文档流分离,操作不会影响Dom树。
2.每次生成临时元素时,都可以保存元素对象的引用,无需多次使用选择器再次获取。
劣势
兼容性仅达到IE9。
四、createElement的优缺点
基本方式
varel=document.createElement
("ElementName"); el.className = ""; el.setAttribute("AttrName",AttrValue); el.setAttribute("AttrName",AttrValue); ... el.appendChild( ... //生成Element的IIFE,见上文 );
优点
1、新创建的元素脱离于文档流,操作不会对Dom树产生影响
2、兼容性***
3、在每一次生成临时Element时候就可以将该Element对象的引用保存下来,而不需要多次用选择器再次获取。
缺点
每一次调用setAttribute方法都是一次次对Element进行修改,此处具有潜在的性能损耗。
五、DomString——临时Element+innerHTML+cloneNode的优缺点
基本模式
var domString2Dom = (function(){ if (window.HTMLTemplateElement){ var container = document.createElement("template"); return function(domString){ container.innerHTML = domString; return container.content.firstChild.cloneNode(true) } }else{ //对不支持的template 的浏览器还有兼容性方法没写,所以不支持tr,td等些元素inner进div中。 var container = document.createElement("div"); return function(domString){ container.innerHTML = domString; return container.firstChild.cloneNode(true) } } })();
var template = domString2Dom('<div class="TestClass" Arg="TestArg"></div>'); for (var index = 0; index < 80; index++) { template.appendChild( (function(){ var el = domString2Dom("<div>M</div>"); return el })() ) }
优点
创建Dom之后不需要多次进行setAttribute
缺点
1、临时元素不能包裹一些特定的元素(不能在所有浏览器的所有 HTML 元素上设置 innerHTML 属性)
2、解析的过程进行了很多其余的操作。此处具有潜在的性能损耗。
3、插入的字符串***层Node只允许有一个元素
六、DomString——XML解析的优缺点
基本模式
var XMLParser = function () { var $DOMParser = new DOMParser(); return function (domString) { if (domString[0] == "<") { var doc = $DOMParser.parseFromString(domString, "application/xhtml+xml"); return doc.firstChild; } else { return document.createTextNode(domString); } }; }();
var template = XMLParser('<div class="TestClass" Arg="TestArg"></div>'); for (var index = 0; index < 80; index++) { template.appendChild((function () { var el = XMLParser("<div>M</div>"); return el; })()); }
优点
DomString方法中通用性***的,虽然IE10+才支持DOMParser,但是IE9以下的有替代方法
缺点
1、解析的过程本身就具有潜在的性能损耗。
2、只能得到刚刚创建最外层元素的克隆。子元素的引用还需要用选择器。
3、插入的字符串***层Node只允许有一个元素
七、临时字符串的优缺点
基本模式:
var template = document.createElement("div"); template.innerHTML = `<div class="TestClass" Arg="TestArg"> Test TextNode ${(function(){ var temp = new Array(8); for (var index = 0; index < 80; index++) { temp[index]="<div>M</div>" } return temp.join() }())} </div>` //需要增加的一大段Element
优点
1、通用性***,不需要逐步创建一大堆无用的Element对象引用
2、运用es6模板字符串编码优雅,不需要字符串用加号进行链接
缺点
1、如果是直接给出配置Config进行渲染需要进行字符串的生成
2、只能得到刚刚创建最外层元素的引用。子元素的引用还需要用选择器。
八、Template元素
由于HTML5中新增了template元素
其特点就是有一个content属性是HTMLDocumentFragment对象,所以可以包容任何元素
基本范式是:
var template = document.createElement("template"); template.innerHTML = `<div class="TestClass" Arg="TestArg"> Test TextNode ${(function(){ var temp = new Array(8); for (var index = 0; index < 80; index++) { temp[index]="<div>M</div>" } return temp.join() }())} </div>` //需要增加的一大段Element // template.content 是HTMLDocumentFragment
优点
比div要好很多,作为临时元素容器的包容性更强
缺点
在不支持的浏览器中表示为HTMLUnknownElement
九、各种方法的效率对比
测试代码:(由于笔者不太熟悉各种浏览器性能的BUG,这里的代码如果有不足请指正),代码由typescript进行编写,也可以用babel进行编译。
/** * @param Count:渲染DOM结构的次数 */ var DateCount = { TimeList : {}, time:function(Str){ console.time(Str); }, timeEnd:function(Str){ console.timeEnd(Str); } }; //==================工具函数====================== var domString2Dom = (function () { var container; if (window.HTMLTemplateElement) { container = document.createElement("template"); return function (domString) { container.innerHTML = domString; return container.content.firstChild.cloneNode(true); }; } else { //对不支持的template 的浏览器还有兼容性方法没写,所以不支持tr,td等些元素inner进div中。 container = document.createElement("div"); return function (domString) { container.innerHTML = domString; return container.firstChild.cloneNode(true); }; } })(); var XMLParser = (function () { var $DOMParser; if (window.DOMParser) { $DOMParser = new DOMParser(); return function (domString) { if (domString[0] == "<") { var doc = $DOMParser.parseFromString(domString, "application/xhtml+xml"); return doc.firstChild; } else { return document.createTextNode(domString); } }; }else{ $DOMParser = new ActiveXObject("Microsoft.XMLDOM"); return function (domString) { if (domString[0] == "<") { $DOMParser.async = false; $DOMParser.loadXML(domString); return $DOMParser } else { return document.createTextNode(domString); } } } })(); //=============================================== var Test = function(Count){ //保留这种写法,能够在移动端平台中不依靠控制台进行效率测试 // var DateCount = { // TimeList : {}, // time:function(Str){ // this.TimeList[Str] = Date.now(); // }, // timeEnd:function(Str){ // alert(Str+(Date.now() - this.TimeList[Str])); // } // } //基准测试1: DateCount.time("无临时div + 不需要字符串拼接 + innerHTML:") for (let index = 0; index < Count; index++) { (function(){ var template = document.createElement("div"); template.className = "TestClass"; template.setAttribute("Arg","TestArg") template.innerHTML = ` Test TextNode <div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div> <div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div> <div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div> <div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div> <div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div> <div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div> <div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div> <div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div> <div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div> <div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div> <div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div> <div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div><div child="true">M</div> ` //需要增加的一大段Element,共100个子级div return template }()) } DateCount.timeEnd("无临时div + 不需要字符串拼接 + innerHTML:") //基准测试2: DateCount.time("createElement+appendChild写法:") for (let index = 0; index < Count; index++) { (function(){ var template = document.createElement("div"); template.className = "TestClass"; template.setAttribute("Arg","TestArg") template.appendChild(document.createTextNode('Test TextNode')); for (let index = 0; index < 100; index++) { let element = document.createElement("div"); element.setAttribute("child","true"); element.appendChild(document.createTextNode("M")) template.appendChild(element) } return template }()) } DateCount.timeEnd("createElement+appendChild写法:") //DocumentFragment DateCount.time("DocumentFragment+ createElement+appendChild 写法:") for (let index = 0; index < Count; index++) { (function(){ var fragment = document.createDocumentFragment(); fragment.appendChild(function(){ var template = document.createElement("div"); template.className = "TestClass"; template.setAttribute("Arg","TestArg") template.appendChild(document.createTextNode('Test TextNode')); for (let index = 0; index < 100; index++) { let element = document.createElement("div"); element.setAttribute("child","true"); template.appendChild(element) } return template; }()); return fragment }()) } DateCount.timeEnd("DocumentFragment+ createElement+appendChild 写法:") //DomString——临时Element+innerHTML+cloneNode // DateCount.time("DomString——临时Element+innerHTML+cloneNode:") // for (let index = 0; index < Count; index++) { // (function(){ // var template = domString2Dom('<div class="TestClass" Arg="TestArg"></div>'); // for (let index = 0; index < 100; index++) { // template.appendChild( // (function(){ // var el = domString2Dom("<div child='true'>M</div>"); // return el // })() // ) // } // return template; // }()) // } // DateCount.timeEnd("DomString——临时Element+innerHTML+cloneNode:") //DomString——XML解析 // DateCount.time("DomString——XML解析:") // for (let index = 0; index < Count; index++) { // (function(){ // var template = XMLParser('<div class="TestClass" Arg="TestArg"></div>'); // for (let index = 0; index < 100; index++) { // template.appendChild((function () { // var el = XMLParser("<div child='true'>M</div>"); // return el; // })()); // } // }()) // } // DateCount.timeEnd("DomString——XML解析:") //临时div + 临时字符串拼接: DateCount.time("临时div + 字符串拼接:") for (let index = 0; index < Count; index++) { (function(){ let template = document.createElement("div"); template.innerHTML = `<div class="TestClass" Arg="TestArg"> Test TextNode ${(function(){ let temp = ""; for (let index = 0; index < 100; index++) { temp+="<div child='true'>M</div>" } return temp }())} </div>` //需要增加的一大段Element return template.firstChild; }()) } DateCount.timeEnd("临时div + 字符串拼接:") //临时template + 临时字符串拼接: DateCount.time("临时template + 字符串拼接:") for (let index = 0; index < Count; index++) { (function(){ var template = document.createElement("template"); template.innerHTML = `<div class="TestClass" Arg="TestArg"> Test TextNode ${(function(){ let temp = ""; for (let index = 0; index < 100; index++) { temp+="<div child='true'>M</div>" } return temp }())} </div>` //需要增加的一大段Element return template.content; }()) } DateCount.timeEnd("临时template + 字符串拼接:") //临时template + createElement+appendChild 写法 DateCount.time("template + createElement+appendChild 写法:") for (let index = 0; index < Count; index++) { (function(){ var template = document.createElement("template"); template.appendChild(function(){ var template = document.createElement("div"); template.className = "TestClass"; template.setAttribute("Arg","TestArg") template.appendChild(document.createTextNode('Test TextNode')); for (let index = 0; index < 100; index++) { let element = document.createElement("div"); element.setAttribute("child","true"); template.appendChild(element) } return template; }()); return template.content }()) } DateCount.timeEnd("template + createElement+appendChild 写法:") }; for (var key of [1,10,100,1000]) { console.log("Start"+key); Test(key); }
“各种动态渲染Element方式的性能分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/93967.html