好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

迷你MVVM框架 avalonjs v5.1发布,性能大幅提高

迷你MVVM框架 avalonjs v5.1发布,性能大幅提高

迷你MVVM框架 avalonjs v5.1发布,性能大幅提高

早在avalon在IE与firefox有较为严重的性能问题,chrome等则由于它们太逆天因此看不出。主要原因是动态插入节点时,each由于一些帮方法考虑不周,结果不得不逐个插入,虽然使用了nextTick进行缓冲,但延迟明显。然后avalonjs v5把大体的架构完成了,然后重点改良这些方法了。在v5.1中所有延迟都没有了,即便在IE6下也很接近之前在chrome的运行效果。算是一次非常出色的改进。

1, addItemView方法不再逐个复制,逐个插入了。

//原来

  function   addItemView(index, item, data) {

         var   scopes = data.scopeList;

         var   collection = data.collection;

         var   parent = data.element;

         var   doc = parent.ownerDocument;

         var   textNodes = [];

         var   scope = createItemModel(index, item, collection, data.args);

         scopes = [scope].concat(scopes)

         for   ( var   node = data.view.firstChild; node; node = node.nextSibling) {

             var   clone = node.cloneNode( true );

             if   (collection.insertBefore) { //必须插入DOM树,否则下为注释节点添加自定义属性会失败

                 parent.insertBefore(clone, collection.insertBefore);

             } else   {

                 parent.appendChild(clone);

             }

             if   (clone.nodeType === 1) {

                 scanTag(clone, scopes.concat(), doc); //扫描元素节点

             } else   if   (clone.nodeType === 3) {

                 textNodes.push(clone); //插值表达式所在的文本节点会被移除,创建循环中断(node.nextSibling===null)

             } else   if   (clone.nodeType === 8) {

                 clone.nodeValue = node.nodeValue + ""   + index;

                 if   (!clone.addScope) {

                     clone.$scope = scope;

                     clone.addScope = "addItemView" ;

                 }

                 clone.$view = data.view.cloneNode( false );

             }

         }

         avalon.nextTick( function () {

             for   ( var   i = 0; node = textNodes[i++]; ) {

                 scanText(node, scopes.concat(), doc); //扫描文本节点

             }

         })

     }

改为

function   addItemView(index, item, data) {

     var   scopes = data.scopes;

     var   list = data.list;

     var   parent = data.element;

     var   doc = parent.ownerDocument;

     var   scope = createItemModel(index, item, list, data.args);

     scopes = [scope].concat(scopes);

     var   view = data.view.cloneNode( true ); //★★★★

     var   textNodes = [];

     var   elements = [];

     for   ( var   node = view.firstChild; node; node = node.nextSibling) {

         if   (node.nodeType === 1) {

             elements.push(node);

         } else   if   (node.nodeType === 3) {

             textNodes.push(node);

         } else   if   (node.nodeType === 8) {

             node.id = node.nodeValue + index; //设置路标

             node.$scope = scope;

             node.$view = view.cloneNode( false ); //★★★★

         }

     }

     // parent.insertBefore(el, null) === parent.appendChild(el)

     parent.insertBefore(view, list.place || null );

 

     for   ( var   i = 0; node = elements[i++];) {

         scanTag(node, scopes.concat(), doc); //扫描文本节点

     }

     avalon.nextTick( function () {

         if   (!parent.inprocess) {

             parent.inprocess = 1; //作用类似于display:none

             var   hidden = parent.hidden; // http://html5accessibility.com/

             parent.hidden = true ; //★★★★ 防止reflow

         }

         for   ( var   i = 0; node = textNodes[i++];) {

             scanText(node, scopes.concat(), doc); //扫描文本节点

         }

         if   (parent.inprocess) {

             parent.hidden = hidden;

             parent.inprocess = 0;

         }

     })

}

2,新的路标系统:avalon使用一个注释节点来确认每个子模板的起点, 像emberjs则是使用两个script节点, knockout是使用两个注释节点 。

//原来

//路标是指每个模板最开头的那个注释节点

     //<!--xxx1--><tag><tag><text><!--xxx2--><tag><tag><text><!--xxx3--><tag><tag><text>

     // 假若 index == 2, 返回<!--xxx2-->

 

     function   findIndex(elem, listName, index) {

         for   ( var   node = elem.firstChild; node; node = node.nextSibling) {

             if   (node.nodeType === 8 && (node.nodeValue === listName + index)) {

                 return   node;

             }

         }

     }

 

     //重置所有路标

 

     function   resetIndex(elem, name) {

         var   index = 0;

         for   ( var   node = elem.firstChild; node; node = node.nextSibling) {

             if   (node.nodeType === 8) {

                 if   (node.nodeValue.indexOf(name) === 0) {

                     if   (node.nodeValue !== name + index) {

                         node.nodeValue = name + index;

                         var   scope = node.$scope || {};

                         scope.$index = index;

                     }

                     index++;

                 }

             }

         }

     }

现在的逻辑简化成这样:

function   findIndex(elem, index) { //寻找路标

     for   ( var   node = elem.firstChild; node; node = node.nextSibling) {

         if   (node.id === node.nodeValue + index) { //★★★★

             return   node;

         }

     }

}

 

function   resetIndex(elem, name) { //重置路标

     var   index = 0;

     for   ( var   node = elem.firstChild; node; node = node.nextSibling) {

         if   (node.nodeType === 8 && node.nodeValue === name) { //★★★★

             if   (node.id !== name + index) {

                 node.id = name + index; //★★★★

                 node.$scope.$index = index;

             }

             index++;

         }

     }

}

3,移除模板的两个函数合并成一个,因此整体代码量都下降了。

//原来

     function   removeItemView(node, listName) {

         var   nodes = [node];

         var   view = node.$view;

         for   ( var   check = node.nextSibling; check; check = check.nextSibling) {

             //遇到下个路标时就断开

             if   (check.nodeType === 8 && check.nodeValue.indexOf(listName) === 0) {

                 break

             }

             nodes.push(check);

         }

         for   ( var   i = 0; node = nodes[i++]; ) {

             view.appendChild(node);

         }

         return   [view, check]; //返回被移除的文档碎片及下一个路标

     }

     //移除each中的多个子视图,返回它们对应的文档碎片集合

 

     function   removeItemViews(node, listName, number) {

         var   views = [];

         do   {

             var   array = removeItemView(node, listName);

             if   (array[1]) {

                 views.push(array[0]);

                 node = array[1];

             } else   {

                 break

             }

         } while   (views.length !== number);

         return   views;

     }

现在是

function   emptyNode(parent) { //它直接用于clear与update方法

     while   (parent.firstChild) {

         parent.removeChild(parent.firstChild);

     }

}

function   removeItemView(node, id) {

     var   nodes = [node];

     var   view = node.$view;

     for   ( var   check = node.nextSibling; check; check = check.nextSibling) {

         if   (check.nodeType === 8 && check.id === id) {

             break

         }

         nodes.push(check);

     }

     for   ( var   i = 0; node = nodes[i++];) {

         view.appendChild(node);

     }

     emptyNode(view); //★★★★

     view = null ; //★★★★

}

可以到 它的主页 查看效果!如果有什么好的改进,记得pull request啊!

 

 

 

标签:  javascript

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于迷你MVVM框架 avalonjs v5.1发布,性能大幅提高的详细内容...

  阅读:39次

上一篇: 尝试MVP模式

下一篇:Wix打包系列