新聞中心
【引自雕刻零碎的博客】一、性能優(yōu)化的原則及方法論

樹(shù)立原則:動(dòng)態(tài)渲染進(jìn)入一個(gè)Dom元素,首先需要保證動(dòng)態(tài)渲染操作必須盡可能少對(duì)原有dom樹(shù)的影響,影響重繪及重排。
確定方法論:必須尋找一個(gè)容器來(lái)緩存渲染期間生成的dom結(jié)構(gòu)(操作必須盡可能少對(duì)原有dom樹(shù)的影響),然后再進(jìn)行一次渲染到目標(biāo)element中。
二、生成期間DOM緩存的選擇
- DocumentFragment(文檔碎片對(duì)象,選擇原因:脫離于文檔流)
- 臨時(shí)Element(選擇原因:新element脫離于文檔流)
- createElement,再一步步進(jìn)行渲染
- 通過(guò)描述Dom的String(下稱(chēng):DomString),轉(zhuǎn)化為Dom對(duì)象
- 臨時(shí)Element+innerHTML+cloneNode返回最外層element元素對(duì)象,再進(jìn)行插入appendChild,必要時(shí)還需要選擇器方法講某一個(gè)Element對(duì)象提取出來(lái)
- XML字符串通過(guò)解析生成Element對(duì)象(注意,不是HTMLxxxElement對(duì)象,是Element對(duì)象),然后將該對(duì)象appendChild進(jìn)去
- 臨時(shí)字符串(選擇原因:借助innerHTML渲染,一次渲染)
三、DocumentFragment的優(yōu)缺點(diǎn)
基本模式:
- var fragment = document.createDocumentFragment();
- fragment.appendChild(
- ... //生成Element的IIFE
- )
- //IIFE示例,根據(jù)配置創(chuàng)建元素
- var ConfigVar = {
- ELname:"div",
- id:"blablabla",
- name:"balblabla",
- class:"ClassName"
- }
- (function(Config){
- var el = document.createElement(Config.ELname);
- el.className = (Config.class || "");
- for (let AttrName in Config){
- if (AttrName == "class")continue;
- el.setAttribute(AttrName,Config[AttrName]);
- }
- return el;
- })(ConfigVar)
優(yōu)點(diǎn)
1、脫離于文檔流,操作不會(huì)對(duì)Dom樹(shù)產(chǎn)生影響
2、在每一次生成臨時(shí)Element時(shí)候就可以將該Element對(duì)象的引用保存下來(lái),而不需要多次用選擇器再次獲取。
缺點(diǎn)
兼容性只是達(dá)到IE9+
http://caniuse.com/#search=DocumentFragment
四、createElement的優(yōu)缺點(diǎn)
基本模式
- var el = document.createElement("ElementName");
- el.className = "";
- el.setAttribute("AttrName",AttrValue);
- el.setAttribute("AttrName",AttrValue);
- ...
- el.appendChild(
- ... //生成Element的IIFE,見(jiàn)上文
- );
優(yōu)點(diǎn)
1、新創(chuàng)建的元素脫離于文檔流,操作不會(huì)對(duì)Dom樹(shù)產(chǎn)生影響
2、兼容性***
3、在每一次生成臨時(shí)Element時(shí)候就可以將該Element對(duì)象的引用保存下來(lái),而不需要多次用選擇器再次獲取。
缺點(diǎn)
每一次調(diào)用setAttribute方法都是一次次對(duì)Element進(jìn)行修改,此處具有潛在的性能損耗。
五、DomString——臨時(shí)Element+innerHTML+cloneNode的優(yōu)缺點(diǎn)
基本模式
- var domString2Dom = (function(){
- if (window.HTMLTemplateElement){
- var container = document.createElement("template");
- return function(domString){
- container.innerHTML = domString;
- return container.content.firstChild.cloneNode(true)
- }
- }else{
- //對(duì)不支持的template 的瀏覽器還有兼容性方法沒(méi)寫(xiě),所以不支持tr,td等些元素inner進(jìn)div中。
- var container = document.createElement("div");
- return function(domString){
- container.innerHTML = domString;
- return container.firstChild.cloneNode(true)
- }
- }
- })();
- var template = domString2Dom('');
- for (var index = 0; index < 80; index++) {
- template.appendChild(
- (function(){
- var el = domString2Dom("
M");- return el
- })()
- )
- }
優(yōu)點(diǎn)
創(chuàng)建Dom之后不需要多次進(jìn)行setAttribute
缺點(diǎn)
1、臨時(shí)元素不能包裹一些特定的元素(不能在所有瀏覽器的所有 HTML 元素上設(shè)置 innerHTML 屬性)
2、解析的過(guò)程進(jìn)行了很多其余的操作。此處具有潛在的性能損耗。
3、插入的字符串***層Node只允許有一個(gè)元素
六、DomString——XML解析的優(yōu)缺點(diǎn)
基本模式
- 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('');
- for (var index = 0; index < 80; index++) {
- template.appendChild((function () {
- var el = XMLParser("
M");- return el;
- })());
- }
優(yōu)點(diǎn)
DomString方法中通用性***的,雖然IE10+才支持DOMParser,但是IE9以下的有替代方法
缺點(diǎn)
1、解析的過(guò)程本身就具有潛在的性能損耗。
2、只能得到剛剛創(chuàng)建最外層元素的克隆。子元素的引用還需要用選擇器。
3、插入的字符串***層Node只允許有一個(gè)元素
七、臨時(shí)字符串的優(yōu)缺點(diǎn)
基本模式:
- var template = document.createElement("div");
- template.innerHTML = `
` //需要增加的一大段Element- Test TextNode
- ${(function(){
- var temp = new Array(8);
- for (var index = 0; index < 80; index++) {
- temp[index]="
M"- }
- return temp.join()
- }())}
優(yōu)點(diǎn)
1、通用性***,不需要逐步創(chuàng)建一大堆無(wú)用的Element對(duì)象引用
2、運(yùn)用es6模板字符串編碼優(yōu)雅,不需要字符串用加號(hào)進(jìn)行鏈接
缺點(diǎn)
1、如果是直接給出配置Config進(jìn)行渲染需要進(jìn)行字符串的生成
2、只能得到剛剛創(chuàng)建最外層元素的引用。子元素的引用還需要用選擇器。
八、Template元素
由于HTML5中新增了template元素
其特點(diǎn)就是有一個(gè)content屬性是HTMLDocumentFragment對(duì)象,所以可以包容任何元素
基本范式是:
- var template = document.createElement("template");
- template.innerHTML = `
` //需要增加的一大段Element- Test TextNode
- ${(function(){
- var temp = new Array(8);
- for (var index = 0; index < 80; index++) {
- temp[index]="
M"- }
- return temp.join()
- }())}
- // template.content 是HTMLDocumentFragment
優(yōu)點(diǎn)
比div要好很多,作為臨時(shí)元素容器的包容性更強(qiáng)
缺點(diǎn)
兼容性不好:http://caniuse.com/#search=HTML%20templates 在不支持的瀏覽器中表示為HTMLUnknownElement
九、各種方法的效率對(duì)比
測(cè)試代碼:(由于筆者不太熟悉各種瀏覽器性能的BUG,這里的代碼如果有不足請(qǐng)指正),代碼由typescript進(jìn)行編寫(xiě),也可以用babel進(jìn)行編譯。
- /**
- * @param Count:渲染DOM結(jié)構(gòu)的次數(shù)
- */
- var DateCount = {
- TimeList : {},
- time:function(Str){
- console.time(Str);
- },
- timeEnd:function(Str){
- console.timeEnd(Str);
- }
- };
- //==================工具函數(shù)======================
- 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 {
- //對(duì)不支持的template 的瀏覽器還有兼容性方法沒(méi)寫(xiě),所以不支持tr,td等些元素inner進(jìn)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){
- //保留這種寫(xiě)法,能夠在移動(dòng)端平臺(tái)中不依靠控制臺(tái)進(jìn)行效率測(cè)試
- // var DateCount = {
- // TimeList : {},
- // time:function(Str){
- // this.TimeList[Str] = Date.now();
- // },
- // timeEnd:function(Str){
- // alert(Str+(Date.now() - this.TimeList[Str]));
- // }
- // }
- //基準(zhǔn)測(cè)試1:
- DateCount.time("無(wú)臨時(shí)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
MMMMMMMMMM MMMMMMMMMM MMMMMMMMMM MMMMMMMMMM MMMMMMMMMM MMMMMMMMMM MMMMMMMMMM MMMMMMMMMM MMMMMMMMMM MMMMMMMMMM MMMMMMMMMM MMMMMMMMMM- ` //需要增加的一大段Element,共100個(gè)子級(jí)div
- return template
- }())
- }
- DateCount.timeEnd("無(wú)臨時(shí)div + 不需要字符串拼接 + innerHTML:")
- //基準(zhǔn)測(cè)試2:
- DateCount.time("createElement+appendChild寫(xiě)法:")
- 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寫(xiě)法:")
- //DocumentFragment
- DateCount.time("DocumentFragment+ createElement+appendChild 寫(xiě)法:")
- 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 寫(xiě)法:")
- //DomString——臨時(shí)Element+innerHTML+cloneNode
- // DateCount.time("DomString——臨時(shí)Element+innerHTML+cloneNode:")
- // for (let index = 0; index < Count; index++) {
- // (function(){
- // var template = domString2Dom('');
- // for (let index = 0; index < 100; index++) {
- // template.appendChild(
- // (function(){
- // var el = domString2Dom("
M");- // return el
- // })()
- // )
- // }
- // return template;
- // }())
- // }
- // DateCount.timeEnd("DomString——臨時(shí)Element+innerHTML+cloneNode:")
- //DomString——XML解析
- // DateCount.time("DomString——XML解析:")
- // for (let index = 0; index < Count; index++) {
- // (function(){
- // var template = XMLParser('');
- // for (let index = 0; index < 100; index++) {
- // template.appendChild((function () {
- // var el = XMLParser("
M");- // return el;
- // })());
- // }
- // }())
- // }
- // DateCount.timeEnd("DomString——XML解析:")
- //臨時(shí)div + 臨時(shí)字符串拼接:
- DateCount.time("臨時(shí)div + 字符串拼接:")
- for (let index = 0; index < Count; index++) {
- (function(){
- let template = document.createElement("div");
- template.innerHTML = `
` //需要增加的一大段Element- Test TextNode
- ${(function(){
- let temp = "";
- for (let index = 0; index < 100; index++) {
- temp+="
M"- }
- return temp
- }())}
- return template.firstChild;
- }())
- }
- DateCount.timeEnd("臨時(shí)div + 字符串拼接:")
- //臨時(shí)template + 臨時(shí)字符串拼接:
- DateCount.time("臨時(shí)template + 字符串拼接:")
- for (let index = 0; index < Count; index++) {
- (function(){
- var template = document.createElement("template");
- template.innerHTML = `
` //需要增加的一大段Element- Test TextNode
- ${(function(){
- let temp = "";
- for (let index = 0; index < 100; index++) {
- temp+="
M"- }
- return temp
- }())}
- return template.content;
- }())
- }
- DateCount.timeEnd("臨時(shí)template + 字符串拼接:")
- //臨時(shí)template + createElement+appendChild 寫(xiě)法
- DateCount.time("template + createElement+appendChild 寫(xiě)法:")
- 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)
- }
- retur
名稱(chēng)欄目:各種動(dòng)態(tài)渲染Element方式的性能探究
標(biāo)題網(wǎng)址:http://fisionsoft.com.cn/article/djojpjd.html


咨詢(xún)
建站咨詢(xún)
