3

我正在寻找优化我的单页应用程序的方法,我目前专注于 jQuery 选择器。

我不是 javascript 专家,但据我了解,将 jquery 选择器的结果存储在变量中以供重复使用比重新查询 dom 的性能要高得多。

因此,例如,而不是这样:

$("#myItem").doSomething();
$("#myItem").doSomethingElse();

这样做更有意义:

var result = $("#myItem");
result.doSomething();
result.doSomethingElse();

此外,据我了解,使用附加到现有 jQuery 对象的 find() 选择器可以显着提高性能,从而减少必要的查询量:

$("#myItem").find(".highlighted");

我有兴趣探索存储我们在应用程序中不断重用的许多选择器的可能性,不仅在局部变量中,而且可能在某个地方的散列或外部函数中。我不知道这是否是最优的,但我很好奇这会收到什么回应。

我尝试(未成功)使用 Underscore.js 的 memoize 函数来执行此操作,但它没有按我预期的方式工作。但是,这个概念看起来像这样:

   jquerySelect = function () {
                var elements = _.memoize(function (selection) {
                    return $(selection);
                });
                return elements;
            };

这里的想法是“return $(selection)”的实际结果工作被执行一次,之后结果被缓存并返回。

我希望能够通过以下方式使用它:

utils.jquerySelect(".highlighted").find("span")

在这个简单的例子中,缓存的关键是“.highlighted”,实用函数知道如何访问结果,从而避免了遍历 DOM 的工作再次完成。我的实现目前不起作用,因为每次都会命中“return”语句。

我不确定这种方法是否有缺陷,但我觉得它可能是。但是,请让我知道您是否看到了一种可行的方法,或者您是否看到了更好的方法。

4

5 回答 5

3

这不是一个坏主意,事实上你已经可以找到许多类似的 JQuery 记忆插件。您的方法面临的主要挑战是知道何时可以使用记忆化结果,以及何时由于 DOM 的变化而失效。由于 JQuery 代码在 DOM 中添加、删除和更改节点的可能性与从 DOM 中选择节点的可能性一样大,因此知道何时必须使记忆缓存中的选择器无效是一个非常重要的问题。

memoize 插件解决这个问题的方式通常是提供一些替代语法,例如$_()对于您想要被记忆的选择器和/或您想要从中检索记忆结果的选择器。无论如何,这会向选择器添加额外的字符串/哈希查找。那么,最后,与本地缓存相比,它的真正优势是什么?

一般来说,最好了解更多关于 JQuery 本质上是如何为 DOM 提供一元转换(也称为链接)的 DSL。

例如,您在问题中的代码可以写成:

$("#myItem").doSomething().doSomethingElse();

您甚至可以使用.end()弹出选择器堆栈,因为在链中结果已经在内部记忆:

$("#myItem").doSomething().find('a').hide().end().doSomethingElse();
// a in #myItem are hidden, but doSomething() and doSomethingElse() happen to #myItem

此外,大多数 JQuery 选择的成本可能被高估。ID 选择(以 开头的选择器#)被传递给快速 DOM 方法,document.getElementById而不是 Sizzle 引擎。重复调用解析为单个 DOM 元素的选择器,例如$(this),对速度没有特别的影响。

如果速度确实是代码中紧密循环中的关注点,那么几乎可以肯定的是,重构该部分而不完全使用 JQuery 选择引擎是值得的。在我的测试中,您可以从本地选择器缓存中获得 10-50% 的性能提升,具体取决于选择器的复杂性,而原生 DOM 选择器相对于 JQuery 选择器的性能提升 1000%。

于 2013-02-08T18:12:15.703 回答
0

我会说这样做更有意义:

var myapp={};
myapp.$body=$(document.body);
myapp.$result = myapp.$body.find("#myItem").doSomething().doSomethingElse();
myapp.$result.find("> .whatever")

关于存储大对象,如果您打算在整个会话中重复使用,我一直在研究它并发现本地商店只允许纯静态对象,因此不会接受 myapp.$result 例如,然后我放弃了这个想法。

于 2013-02-08T18:25:09.970 回答
0

我可以专门谈论 memoize,但我可以说您维护缓存的 jQuery 集合的关联数组的策略是一个好主意。话虽如此,您需要考虑以下几点:

  1. 缓存一个只会在前面使用 0 或 1 次的选择器可能并不总是值得的。
  2. 如果您尝试计算有关元素集合的动态内容(例如长度),则需要在获取值之前重新选择/重新缓存元素。例如:

    var $div = $("div");

    $div.长度;//说是1

    //...一些向页面添加一些 div 的代码

    $div.长度;//还是1!

  3. 我会考虑“及时”进行缓存。换句话说,仅在第一次需要时缓存该项目。

于 2013-02-08T18:10:34.137 回答
0

总体思路是好的。正如其他人已经说过的那样,你需要小心你没有缓存特别需要重新查询的东西。

您尝试失败的原因是您使用_.memoize不正确。它看起来像这样:

var jquerySelect = _.memoize(function (selection) {
                       return $(selection);
                   });

memoize返回一个您将调用的函数。它本质上是围绕处理缓存的函数创建一个包装器。

于 2013-02-08T18:21:28.123 回答
-1

缓存是在 jquery 中遵循的最佳实践。

我将缓存分为两类。

1. local cahching:其范围仅在某个函数内。

前任:

function doSome(){
var elm=$('selector');
elm.something1().something2()  .... chain it on one
}

使用时要记住的几点:

一世。缓存由极少数函数访问或仅由该函数访问的元素。

ii. 当动态添加元素时,您每次都需要重新初始化它,因此本地缓存更好。

iii. 将所有 var 声明与用于缓存的变量一起保留在顶部。

iv. 缓存一次,稍后使用.filter('selector')or .filter(function) ex 过滤。elm.filter(':checked').dosomething();

2. 全局缓存作用域为所有函数

对于全局缓存,声明一个对象并将所有缓存作为该变量中的键值对。

前任:

var global={};
$(document).ready(function(){
global.sel1=$('selector1');
global.sel2=$('selector2');
});

//请记住,如果您在页面顶部声明全局变量,请始终在文档就绪函数中添加 cahin 变量。由于元素不会出现在 dom 中,所以选择器将返回 undefined。

如果您将所有脚本放在我喜欢的页面下方,而不是您可以使用

var global={
sel1:$('selector1'),
sel2:$('selector2')
} ;

要记住的要点

  1. 包装器元素最好在全局缓存中缓存。作为子元素,您可以使用 .find() 找到并且通常不会实际修改。(我建议保留所有带有 id 的包装元素,因为 id 是最快的选择方式。)
  2. 仅用于经常使用的元素。不要将它用于稍后将附加的元素的选择器。
  3. 在函数中创建全局对象的本地引用,因为访问全局事物比访问本地事物更昂贵。例如:函数 some(){ var elm=global.sel1; //现在用榆树做点什么 }

用于全局 chaching 的 setter 和 getter 函数

function getGlobal(selector){
//if not present create it .
if(!global[selector]){
global[selector]=$(selector);
}
return global[selector];
}

//to use it
var elm=getGlobal('selector');
于 2013-02-08T19:21:33.223 回答