zoukankan      html  css  js  c++  java
  • 提高QWrap性能的决窍

    近日,阮一峰同学参考jquery团队的Addy Osmani的一篇《提高jQuery性能的诀窍》整理了一份《JQuery的最佳实现》,文中提到很多jquery使用者可能会用到的搞高性能的技巧。其实在QWrap的使用中,也会有很多类似的问题,在这里就生吞活剥的来一篇《提高QWrap性能的决窍》。

    百度百科:生吞活剥 喻生硬地抄袭或机械地搬用经验、方法、理论等。也指生拉硬扯。

    总之,本文结构与大部分内容系抄袭阮同学的那篇文章。

    《提高QWrap性能的决窍》

    1. 使用最新版本的QWrap
    QWrap的最新稳定版本是1.0,可以关注github.com/wedteam/qwrap上的更新,


    2. 用对选择器

    与Jquery一样,在QWrap中,你可以用多种选择器,选择同一个网页元素。每种选择器的性能是不一样的,你应该了解它们的性能差异。

    (1)最快的选择器:id选择器和元素标签选择器

    举例来说,下面的语句性能最佳:

      W('#id')
      W(
    'form')
      W(
    'input')

    遇到这些选择器的时候,QWrap内部会自动调用浏览器的原生方法(比如getElementById()),所以它们的执行速度快。

    (2)较慢的选择器:class选择器

    W('.className')的性能,取决于不同的浏览器。

    Firefox、Safari、Chrome、Opera浏览器,都有原生方法querySelectorAll(),所以速度并不慢。但是,IE5-IE7都没有部署这个方法,所以这个选择器在IE中会相当慢。

    (3)最慢的选择器:伪类选择器和属性选择器

    先来看例子。找出网页中所有的隐藏元素,就要用到伪类选择器:

      W(':nth-child(2n)')

    属性选择器的例子则是:

      W('[attribute=value]')

    这两种语句是最慢的,因为浏览器没有针对它们的原生方法。一些浏览器的新版本,增加了querySelector()和querySelectorAll()方法,但是QWrap.Selector也采用了保守策略,在出现伪类选择器与属性选择器时,也并没有完全使用querySelectorAll()。
    使用querySelector的具体策略是:只有关系符(“,”“ ”“>”“+”“~”)与快捷选择器(div#id.className)。

    3. 理解子元素和父元素的关系

    下面六个选择器,都是从父元素中选择子元素。你知道哪个速度最快,哪个速度最慢吗?

      W(g('parent')).query('>.child')
      W(
    '#parent > .child')
      W(
    '.child', g('parent'))
      W(g(
    'parent')).query('.child')
      W(
    '#parent .child')
      W(
    '.child', W('#parent'))

    我们一句句来看。

    W(g('parent')).query('>.child') 与 W('#parent > .child')的意图是,在#parent的儿子里找.child,不包括孙子曾孙玄孙等。
     网上传说很多Sizzle的顺序是从右往左的,其实不尽然,如果是从右往左,“W('#parent > .child')”会明显慢于“W(g('parent')).query('>.child')”,事实上,选择器的实现者都会在某些情况下调整或打乱查找方向,例如出现#id时,会从#id所在处打断成两个selector。当然QW.Selector也会这么做,这样做的结果就是,上面两者的效率相差不大。
     W(g('parent')).query('>.child') getElementById后进行W装箱,装箱后querySelectorAll('>child'),后来将结果再进行W装箱。
     W('#parent > .child') 一次查询,将结果进行W装箱。在不支持querySelectorAll的浏览器里,查询中也会用到getElementById。分析selector字符串的时间与装箱时间没多大区别。
     这里提到了W装箱,其实就是把一个元素或是一个元素数组,变成一个NodeW对象。相关介绍,参见QWrap系列介绍中的Wrap部分。
    以下几个是在子孙中查找.child元素。
    (1) W('.child', g('parent'))---- 一次getElementById,一次query,一次W装箱,效率最快
    (2) W('#parent .child')----一次query,但是query里需要解析出getElementById,然后再查询,最后将结果装箱,略慢于上一种,不过,这种写法是最简写法哦。
    (3) W(g('parent')).query('.child') 一次getElementById,随后装箱,再query一次,再对结果装箱,与2的效率相差不大
    (4) W('.child', W('#parent')) 两次query,两次装箱,会比2、3略慢
    事实上,在QWrap的Selector的优化三条里有说明:最左边的自选器要带tagName,所以,如果确定要找的是div的话,最好是能这么写:W('#parent div.child')

    4. 不要过度使用W

    W速度再快,也无法与原生的Dom操作方法相比。所以有原生方法可以使用的场合,尽量避免使用W。

    请看下面的例子,为a元素绑定一个处理点击事件的函数:

      W('a').click(function(){
        alert(W(
    this).attr('id'));
      });

    这段代码的意思是,点击a元素后,弹出该元素的id属性。为了获取这个属性,必须进行W装箱,而后attr('id')。

    事实上,这种处理完全不必要。更正确的写法是,直接采用原生写法,调用this.id:

      W('a').click(function(){
        alert(
    this.id);
      });

    this.id的速度会比W(this).attr('id')快了N多倍。

    5. 做好缓存

    选中某一个网页元素,是开销很大的步骤。所以,使用选择器的次数应该越少越好,并且尽可能缓存选中的结果,便于以后反复使用。

    比如,下面这样的写法就是糟糕的写法:

      W('#top').query('p.classA');
      W(
    '#top').query('p.classB');

    更好的写法是:

      var cached = W('#top');
      cached.query(
    'p.classA');
      cached.query(
    'p.classB');


    6. 使用链式写法

    像jQuery一样,QWrap里支持链式写法。

      W('div').css('color','red').query('h3').item(2).html('Hello');

    采用链式写法时,可以省去许多变量定义。

    7. 事件的委托处理(Event Delegation)

    javascript的事件模型,采用"冒泡"模式,也就是说,子元素的事件会逐级向上"冒泡",成为父元素的事件。

    利用这一点,可以大大简化事件的绑定。比如,有一个表格(table元素),里面有100个格子(td元素),现在要求在每个格子上面绑定一个点击事件(click),请问是否需要将下面的命令执行100次?

      W("td").on("click", function(){
        $(
    this).toggleClass("click");
      });

    回答是不需要,我们只要把这个事件绑定在table元素上面就可以了,因为td元素发生点击事件之后,这个事件会"冒泡"到父元素table上面,从而被监听到。

    因此,这个事件只需要在父元素绑定1次即可,而不需要在子元素上绑定100次,从而大大提高性能。这就叫事件的"委托处理",也就是子元素"委托"父元素处理这个事件。

    具体的写法是:

      $("#table").delegate("td", "click", function(){
        $(
    this).toggleClass("click");
      });


    委托处理会提高事件绑定效率,但是会降低事件发生后的运行效率,因为需要递归向上寻亲并matchesSelector。

     

    8. 少改动DOM结构

    改动DOM结构开销很大,因此不要频繁使用.appendChild()、.insertBefore()和.insetAfter()这样的方法。


    9. 正确处理循环

    循环总是一种比较耗时的操作,原生的for循环会比forEach/map少一层函数调用,所以效率会好一些。
    在使用forEach/map时,也需要注意回调函数的效率。
    例如:

    var values = W('input[name=aaa][checked]').map(function(el){
    return W(el).val();
    });

    更快一点的写法是:

    var values = W('input[name=aaa][checked]').map(function(el){
    return el.value;
    });

    QWrap还有意外的惊喜哦,这里有一种更快更省的写法:

    var values = W('input[name=aaa][checked]').getValueAll();


    10. 尽量少生成W对象

    W对象是对一个或一组节点进行装箱操作后产生的NodeW对象,是一个对象实例,会占用不少资源。所以,尽量少生成W对象。

    举例来说,许多节点方法都有三个版本:NodeW原型方法、NodeW静态方法(可以是数组、节点或节点id)、Dom静态方法(是只针对节点或节点id)。
    例如,以下代码可以获取value:

    W('#myId').val(); //会产生NodeW实例
    W('#myId').getValue(); //会产生NodeW实例
    W.getValue(g('myId'));
    W.getValue(
    'myId');
    Dom.getValue(
    'myId');

    事实上,后面的三种方法都可以得到不会产生W实例,效率都比前两种方法快。但是,我们并不推荐使用他们。
    因为,很多情况下,效率问题经常是伪命题。
    也就是说:在发现有效率问题之前,我们完全可以大胆的用最简省的写法。因为这样我们可以少敲几个字。------我们都是爱偷懒的程序员。

  • 相关阅读:
    查找大文件的命令
    JavaScript对象参考手册
    Thymeleaf(Java模板引擎)
    C#基础语法补充
    mysql 拾遗提高(函数、事务、索引)
    jQuery总结
    Hibernate (开源对象关系映射框架)
    git的使用命令
    Xpath,XQuery,DTD
    XML DOM(Document Object Model)
  • 原文地址:https://www.cnblogs.com/jkisjk/p/qwrap_nodew_best_practices.html
Copyright © 2011-2022 走看看