Asset.images慢?如何执行功能以便它们不会冻结浏览器?

时间:2012-02-06 14:03:43

标签: javascript mootools

我最近下载了一个不错的mootools插件,为我的网站上的搜索结果提供评分系统:MooStarRating

它工作得很好,但初始化起来很慢。以下是我记录的执行时间(用于撤回50个搜索结果)。

======== starrating ========
init:     0.06ms   50
img:      5.40ms   50
str:      0.54ms   50
each:     3.04ms   50
inject:   0.86ms   50
end:      1.52ms   50
subtotal: 11.42ms  50
-----------------
total:    571.00ms

以下是这些日志引用的initialize函数(仅供参考):

initialize: function (options) {

    lstart("starrating");

    // Setup options
    this.setOptions(options);

    // Fix image folder
    if ((this.options.imageFolder.length != 0) && (this.options.imageFolder.substr(-1) != "/"))
        this.options.imageFolder += "/";

    // Hover image as full if none specified
    if (this.options.imageHover == null) this.options.imageHover = this.options.imageFull;

    lrec("init");

    // Preload images
    try {
        Asset.images([
        this.options.imageEmpty,
        this.options.imageFull,
        this.options.imageHover
    ]);
    } catch (e) { };

    lrec("img");

    // Build radio selector
    var formQuery = this.options.form;
    this.options.form = $(formQuery);
    if (!this.options.form) this.options.form = $$('form[name=' + formQuery + "]")[0];
    if (this.options.form) {
        var uniqueId = 'star_' + String.uniqueID();
        this.options.form.addClass(uniqueId);
        this.options.selector += 'form.' + uniqueId + ' ';
    }
    this.options.selector += 'input[type=radio][name=' + this.options.radios + "]";
    // Loop elements
    var i = 0;
    var me = this;
    var lastElement = null;
    var count = $$(this.options.selector).length;
    var width = this.options.width.toInt();
    var widthOdd = width;
    var height = this.options.height.toInt();
    if (this.options.half) {
        width = (width / 2).toInt();
        widthOdd = widthOdd - width;
    }

    lrec("str");

    $$(this.options.selector).each(function (item) {

        // Add item to radio list
        this.radios[i] = item;
        if (item.get('checked')) this.currentIndex = i;

        // If disabled, whole star rating control is disabled
        if (item.get('disabled')) this.options.disabled = true;

        // Hide and replace
        item.setStyle('display', 'none');
        this.stars[i] = new Element('a').addClass(this.options.linksClass);
        this.stars[i].store('ratingIndex', i);
        this.stars[i].setStyles({
            'background-image': 'url("' + this.options.imageEmpty + '")',
            'background-repeat': 'no-repeat',
            'display': 'inline-block',
            'width': ((this.options.half && (i % 2)) ? widthOdd : width),
            'height': height
        });
        if (this.options.half)
            this.stars[i].setStyle('background-position', ((i % 2) ? '-' + width + 'px 0' : '0 0'));
        this.stars[i].addEvents({
            'mouseenter': function () { me.starEnter(this.retrieve('ratingIndex')); },
            'mouseleave': function () { me.starLeave(); }
        });

        // Tip
        if (this.options.tip) {
            var title = this.options.tip;
            title = title.replace('[VALUE]', item.get('value'));
            title = title.replace('[COUNT]', count);
            if (this.options.tipTarget) this.stars[i].store('ratingTip', title);
            else this.stars[i].setProperty('title', title);
        }

        // Click event
        var that = this;
        this.stars[i].addEvent('click', function () {
            if (!that.options.disabled) {
                me.setCurrentIndex(this.retrieve('ratingIndex'));
                me.fireEvent('click', me.getValue());
            }
        });

        // Go on
        lastElement = item;
        i++;

    }, this);

    lrec("each");

    // Inject items
    $$(this.stars).each(function (star, index) {
        star.inject(lastElement, 'after');
        lastElement = star;
    }, this);

    lrec("inject");

    // Enable / disable
    if (this.options.disabled) this.disable(); else this.enable();

    // Fill stars
    this.fillStars();

    lrec("end");

    return this;
},

因此,函数中最慢的部分是:

    // Preload images
    try {
        Asset.images([
        this.options.imageEmpty,
        this.options.imageFull,
        this.options.imageHover
    ]);
    } catch (e) { };

这很奇怪。 Asset.images做了什么?在浏览器加载这些图像之前,脚本是否会阻塞?有没有办法预加载运行得更快的图像?

如何让我页面上的脚本执行得更快?对于他们来说,执行800毫秒是个大问题,但200毫秒仍然很糟糕。目前,我的搜索结果一下子就存在了。是否可以单独创建单个搜索结果,以便在创建时不阻止浏览器?同样,是否可以针对搜索结果的各个组件执行此操作,例如MooStarRating插件?

1 个答案:

答案 0 :(得分:1)

没有。 Asset.images是非阻塞的,因为它们中的每一个都是单独加载的,并且在完成所有操作时都会调度一个单一事件。

加载的速度取决于浏览器,但它将具有多线程功能,无论从同一主机并行下载的能力如何。

https://github.com/mootools/mootools-more/blob/master/Source/Utilities/Assets.js#L115-129

立即返回一个Element集合,其中包含可能仍在下载的PROMISE元素。没关系 - 你可以用它来注入els,附加事件,类等等 - 你只是无法读取像宽度,高度这样的图像属性。

每个单独的图片都有自己的onload,它会触发onProgress,当完成所有操作时,我会建议你启用它,删除try / catch阻止并查看哪个图像正在创建延迟。你肯定不需要等待Asset.images的任何东西回来。

你似乎也将它用作'主要缓存'方法,而不是任何东西 - 因为你并没有真正将引用保存到类实例中。您的“每个”迭代可能会被优化,因此如果对象和函数被缓存,它会占用一半的时间,因此引用也是如此。如果你可以使用事件委托可能更多。

由于javascript的单线程性质,为了回答有关不冻结浏览器的问题,您可以通过setTimeout(或mootools中的Function.delay)将代码设置为0或10ms(由于浏览器解释)而延迟代码。你也可以编写函数来完成一个回调,你可以在其中传递函数结果(如果有的话)(想想ajax!)。