我正在寻找优化我的单页应用程序的方法,目前我专注于jQuery选择器。
我不是javascript专家,但我的理解是,将jquery选择器的结果存储在变量中以供重用比重新查询dom更具性能。
因此,例如,代替这个:
$("#myItem").doSomething();
$("#myItem").doSomethingElse();
这样做更有意义:
var result = $("#myItem");
result.doSomething();
result.doSomethingElse();
此外,我的理解是,使用附加到现有jQuery对象上的find()选择器可以减少必要的查询量,这对性能有很大好处:
$("#myItem").find(".highlighted");
我感兴趣的是探索将我们在应用程序中不断重复使用的许多选择器存储在本地变量中的可能性,也可能存储在某个地方的散列或外部函数中。我不知道这是否是最佳的,但我很好奇这会得到什么回应。
我曾尝试(没有成功)使用Underscore.js的memize函数来执行此操作,但它并没有按我预期的方式工作。但是,这个概念看起来是这样的:
jquerySelect = function () {
var elements = _.memoize(function (selection) {
return $(selection);
});
return elements;
};
这里的想法是"return$(selection)"的实际结果工作执行一次,之后缓存并返回结果。
我希望能够以以下方式使用它:
utils.jquerySelect(".highlighted").find("span")
在这个简单的例子中,缓存的关键是".hoblighted",实用程序函数知道如何访问结果,从而避免了再次遍历DOM的工作。我的实现目前不起作用,因为每次都会碰到"return"语句。
我不确定这种方法是否有缺陷,但我感觉可能有缺陷。但是,如果你能找到一种方法,或者你能找到更好的方法,请告诉我。
这不是一个坏主意,事实上,你已经可以在那里找到几十个类似的JQuery记忆插件了。您的方法面临的主要挑战是知道何时可以使用已存储的结果,以及何时由于DOM的更改而使其无效。由于JQuery代码在DOM中添加、删除和更改节点的可能性与从DOM中进行选择的可能性一样大,因此知道何时必须使内存缓存中的选择器无效是一个不小的问题。
记忆插件解决这一问题的方法通常是提供一些替代语法,例如$_()
,用于您想要记忆和/或从中检索记忆结果的选择器。无论如何,这会为选择器添加额外的字符串/哈希查找。那么,最终,与本地缓存相比,这种方式的真正优势是什么呢?
一般来说,最好更多地了解JQuery本质上是一个DSL,它为DOM提供一元转换(也称为链接)。
例如,问题中的代码可以写为:
$("#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选择(以#
开头的选择器)被传递给像document.getElementById
这样的快速DOM方法,而不是Sizzle引擎。对解析为单个DOM元素的选择器(例如$(this)
)的重复调用对速度没有特别的影响。
如果速度真的是代码中紧密循环中的问题,那么完全不使用JQuery选择引擎来重构该部分几乎是值得的。在我的测试中,根据选择器的复杂性,您可以从本地选择器缓存中获得10-50%的性能增益,从本地DOM选择器中获得1000%的性能增益。
我可以具体谈谈内存化,但我可以说,您维护缓存jQuery集合的关联数组的策略是个好主意。话虽如此,您需要考虑以下几点:
- 缓存一个只提前使用0或1次的选择器可能并不总是值得的
-
如果您试图计算元素集合的动态内容(例如长度),则需要在获取值之前重新选择/缓存元素。例如:
var$div=$("div");
$div.length//说是1
//。。。在页面中添加一些div的一些代码
$div.length//还是1!
-
我会考虑"及时"进行缓存。换句话说,只在第一次需要时缓存项目。
总体思路不错。不过,正如其他人已经指出的那样,您需要小心,不要缓存特别需要重新查询的内容。
尝试失败的原因是您未正确使用_.memoize
。它看起来是这样的:
var jquerySelect = _.memoize(function (selection) {
return $(selection);
});
memoize
返回一个函数,然后您将调用该函数。它本质上是围绕处理缓存的函数创建一个包装器。
我想说这样做更有意义:
var myapp={};
myapp.$body=$(document.body);
myapp.$result = myapp.$body.find("#myItem").doSomething().doSomethingElse();
myapp.$result.find("> .whatever")
关于存储大对象,如果你想在整个会话中重用,我一直在研究它&发现本地商店只允许纯静态对象,所以不会接受myapp$结果,例如,然后我放弃了这个想法。
缓存是jquery中要遵循的最佳实践。
我将缓存分为两类。
1.局部cahching:其作用域仅在某个函数内。
例如:
function doSome(){
var elm=$('selector');
elm.something1().something2() .... chain it on one
}
使用时要牢记的要点:
i。以缓存由很少的函数访问的元素,或者说仅由该函数访问。
ii。当动态添加元素时,每次都需要重新初始化它本地缓存更好。
iii。将所有var声明与用于缓存的变量放在首位。
iv。缓存一次,稍后使用.filter('selector')
或.filter(function)
进行筛选例如elm.filter(':checked').dosomething();
2.全局缓存其范围是的所有功能
对于全局缓存,声明一个对象,并将所有缓存作为该变量中的键值对。
例如:
var global={};
$(document).ready(function(){
global.sel1=$('selector1');
global.sel2=$('selector2');
});
//如果您在页面顶部声明全局变量,请记住始终在document-ready函数中添加cahing变量。由于元素将不存在于dom中,因此选择器将返回未定义的。
如果你把所有的脚本放在页面的下面,我更喜欢你可以使用
var global={
sel1:$('selector1'),
sel2:$('selector2')
} ;
需要记住的要点
- 包装器元素最好在全局缓存中缓存。作为子元素,您可以使用.find()找到,并且通常不会进行实际修改。(我建议保留所有带有id的包装器元素,因为id是最快的选择方式。)
- 仅用于常用元素。不要将它用作选择器,稍后将附加哪个元素
- 在函数中创建全局对象的本地引用,因为访问全局对象的成本高于访问本地对象。例如:函数some(){var elm=global.sel1;//现在和elm做点什么}
全局查询的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');