【问题标题】:Reduce CPU usage in jQuery DOM manipulation减少 jQuery DOM 操作中的 CPU 使用率
【发布时间】:2012-03-18 05:45:11
【问题描述】:

我可以对这段代码做些什么来提高它的 CPU 效率(它现在占用了我大约 80% 的 CPU)?我昨天学习了javascript,所以可能只是我没有经验。此代码控制相当大的图块数组的转换。在鼠标悬停时,瓷砖翻转并在鼠标悬停时翻转回来。将有几个线程同时运行,我看不出有办法绕过那个。 我使用这个脚本是因为我需要以 webkit-transitions 不支持的方式准确控制转换的作用。希望 cmets 足够有意义,可以对代码有所了解。该函数是实时的,因为在加载页面时会在 javascript 中创建图块数组。之后,不再创建瓷砖。

来源可以在这里找到。我还没有工作上传。 wikisend.com/download/811662/test.zip

谢谢。

    //By default, javascript will not complete a hover transition unless the mouse 
remains over the entire duration of the transition. This scrip will force the hover 
transition to completion.
$(document).ready(function() {
    $('.tile').live('mouseenter mouseleave', (function() {

    if (event.type == 'mouseover') {
        var $this = $(this);
        $this.addClass('hover');

        //prevents mouseleave from happening when user re-enters after exiting before time is up
        $this.data('box-hover-hovered', false);
        //tile is not ready for leaving hover state
        $this.data('box-hover-not-ready', true);
        var timeout = setTimeout(function() {
            //box will be ready after time is up
            var state = $this.data('box-hover-hovered');
            if (state) { //this is entered when user exits before
                //time is up
                $this.removeClass('hover');
            }
            $this.data('box-hover-not-ready', false);
            //it's ready
        }, 400); // .1 second
        // Remove previous timeout if it exists
        clearTimeout($this.data('box-hover-timeout'));
        //stores current timer id (current timer hasn't executed yet)
        $this.data('box-hover-timeout', timeout);
    }

    else {
        var $this = $(this);

        // If not not-ready, do nothing
        // By default, the value is `undefined`, !undefined === true
        var not_ready = $this.data('box-hover-not-ready');
        if (!not_ready) {
            //if user remains hovering until time is up.
            $this.removeClass('hover');
        } else {
            //user would not have completed the action
            $this.data('box-hover-hovered', true);
        }
    }
}));
});​

【问题讨论】:

  • 你有这个演示页面吗?
  • 如果您要做的只是确保hover 类在对象首次悬停后至少保持应用于对象 400 毫秒,那么有很多更简单的方法可以这样做会使用更少的 CPU。但是,在写出这样的答案之前,您能否确认这就是您尝试使用此代码完成的全部工作?
  • 对我来说重要的是,即使用户在前 400 毫秒内将鼠标移开,从无悬停到悬停的过渡始终会完成。悬停到非悬停状态是可逆的,并且遵循转换的默认行为。所以是的,你在说什么听起来是正确的。
  • 好的,我写了一个符合您要求的答案。虽然我在编写我的实现时没有查看您的实现(我只是从预期的目标出发),但我的实现与您的相似,但在一些方面更简单。

标签: javascript performance live transitions


【解决方案1】:

好的,如果您要做的是确保在取消悬停之前完成无悬停到悬停过渡,您可以这样做:

$(document).ready(function() {
    $(document.body).on('mouseenter mouseleave', '.tile', function(event) {
        var $this = $(this);
        if (event.type == 'mouseenter') {
            $this.data("hovering", true);
            if (!$this.hasClass('hover')) {
                $this.addClass('hover');
                var timeout = setTimeout(function() {
                    $this.removeData("timer");
                    if (!$this.data("hovering")) {
                        $this.removeClass('hover');
                    }
                }, 400);
                $this.data("timer", timeout);
            }
        } else {
            $this.data("hovering", false);
            // if no timer running, then just remove the class now
            // if a timer is running, then the timer firing will clear the hover
            if (!$this.data("timer")) {
                $this.removeClass('hover');
            }
        }
    });
});​

这是一个包含完整代码 cmets 的工作演示:http://jsfiddle.net/jfriend00/rhVcp/

这是对其工作原理的详细解释:

  • 首先,我切换到.on(),因为.live() 现在对于所有版本的jQuery 都已弃用。您应该将document.body 替换为.tile 静态对象的最近祖先。
  • 对象保留一个.data("hovering", true/false) 项,它始终告诉我们鼠标是否在对象上,与.hover 类状态无关。这在计时器触发时是必需的,因此我们知道此时需要将真实状态设置为什么。
  • mouseenter 事件发生时,我们检查hover 类是否已经存在。如果是这样,那就没什么可做的了。如果hover 类不存在,那么我们添加它。由于它以前不存在并且我们只是添加了它,因此这将是 hover 转换的开始。
  • 我们为过渡的长度设置了一个计时器,并在对象上设置了一个.data("timer", timeout) 项,以便将来的代码可以知道计时器已经在运行。
  • 如果我们在此计时器触发之前获得mouseleave,我们将看到.data("timer") 存在并且我们将什么都不做(从而允许过渡完成)。
  • 当计时器触发时,我们执行.removeData("timer") 以摆脱该标记,然后我们查看是否仍然悬停在.data("hovering") 上。如果我们不再悬停(因为mouseleave 发生在计时器运行时),我们执行.removeClass("hover") 将对象置于所需状态。如果我们碰巧还在悬停,我们什么也不做,因为我们还在悬停,所以对象已经处于正确的状态。

简而言之,当我们开始悬停时,我们设置悬停状态并启动计时器。只要该计时器正在运行,我们就不会更改对象的状态。当计时器触发时,我们设置对象的正确状态(悬停或不悬停,取决于鼠标的位置)。这保证了悬停状态将至少在转换的时间内保持不变,并且当最短时间过去时(因此转换完成)​​,我们更新对象的状态。

我非常小心地没有使用任何全局变量,因此这可以在多个 .tile 对象上工作,而不会在它们之间产生任何干扰。

在一个重要的设计点中,您永远不能让多个计时器运行,因为只有在悬停类不存在时才会设置计时器,我们现在只是添加它,一旦计时器运行,我们永远不会删除悬停类,直到计时器完成。因此,一旦一个定时器运行,就没有设置另一个定时器的代码路径。这简化了逻辑。这也意味着计时器仅从第一次应用悬停类时开始运行,这保证了我们只从第一次应用悬停类时强制执行时间。

至于性能,CSS 转换将占用它占用的任何 CPU - 这取决于浏览器的实现,对此我们无能为力。为了最小化 CPU 负载,我们所能做的就是确保我们在每次鼠标移入/移出时尽可能地做到最小,并尽可能避免 DOM 操作,因为它们通常是最慢的操作类型。在这里,我们仅在 hover 类尚不存在时添加它,并且仅在时间到期且鼠标不再位于其上方时才将其删除。其他一切都只是 .data() 操作,它们只是 JavaScript 哈希表操作,应该非常快。这应该只在需要时触发浏览器重排,这是我们能做的最好的。

选择器性能在这里应该不是问题。这是委托事件处理,唯一正在实时检查的选择器(在事件发生时)是 .tile,这是一个非常简单的检查(只需检查 event.target 是否具有该类 - 不需要其他对象对性能很重要的一件事是为委托的事件绑定选择尽可能接近“.tile”的祖先,因为这将花费更少的时间在事件被处理之前冒泡,并且您不会以条件结束其中有很多委托事件都绑定到同一个对象,这可能会很慢,这就是不推荐使用 .live() 的原因。

【讨论】:

  • 首先,感谢您花时间编写代码。我把你的代码换成了我的,它仍然可以正常工作。 CPU 仍然很大,这让我尝试了一些简单的方法来测试实际完成工作的位置。它不再符合我的目的,但它的 CPU 甚至超高。我相信CSS是问题所在。我只在我的活动监视器(在 osx lion 上)中查看 %CPU,它会很快跳到 90 年代中期。对于 CSS 来说,这似乎高得无法接受,你同意吗?
  • 我猜 CSS 位确实回答了这个问题。很遗憾,如果不减少 CSS,我就无法进一步减少(据我所知)。
  • @Chet - 我现在似乎无法访问your test page。可能有一些方法可以在不改变视觉效果的情况下简化 CSS 速度。
  • 我知道它适用于我的 chrome。另一台计算机上的朋友刚刚验证它可以在 safari 上运行。你运行的是什么浏览器?我不保证任何其他人。
  • 它正在工作。我什至无法更早地访问互联网上的页面。我认为你最好的选择是找到一个对你来说看起来不错的非 3D 过渡。事实上,你正在做一个非常复杂的方法,只是让边缘动画到中间。我敢打赌,你可以找到一个看起来相似但使用更少 CPU 的非 3D 过渡。
【解决方案2】:

编辑:见下面 dbaupp 的评论。

马上我会说使用更具体的选择器,而不仅仅是 $('.tile')。这只是意味着将您的选择器更改为 $('div.tile') 或更好的 $('#someparentid div.tile') 之类的东西。

这样您就不必遍历整个 DOM 来搜索匹配的类。

恐怕 Mikko 似乎很正确 http://jsperf.com/jquery-right-to-left-selectors

显然我的解决方案是一个古老的误解。 Good ways to improve jQuery selector performance?

加速该选择器的唯一方法是通过 id 调用它(例如,$('#tile')),不幸的是,鉴于您可能有多个图块,这似乎不是适合您的解决方案元素。

【讨论】:

  • CSS 选择器是从右到左评估的,您的示例实际上会减慢代码速度。
  • 关于 CSS 性能的事实perfectionkills.com/…
  • jQuery 将尽可能回退到原生 querySelectorAll(),因此同样的含义也适用于 jQuery
  • 如果你缓存上下文,即var context = $("#someparent"); $(".tile", context);,那么你将获得比$(".tile")更好的性能:jsperf.com/jquery-right-to-left-selectors/2
猜你喜欢
  • 2015-08-02
  • 1970-01-01
  • 2013-06-01
  • 2016-01-07
  • 1970-01-01
  • 2013-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多