我很难调试新闻自动收录器 - 我是从头开始使用JavaScript编写的。
除了IE9(以及一些移动浏览器 - Opera Mobile)之外,它在大多数浏览器上运行良好,而且移动速度非常慢。
使用开发人员工具> Profiler使我能够找到问题的根本原因。
调用offsetLeft
来确定是否旋转自动收报机,即第一个元素成为最后一个元素。
function NeedsRotating() {
var ul = GetList();
if (!ul) {
return false;
}
var li = GetListItem(ul, 1);
if (!li) {
return false;
}
if (li.offsetLeft > ul.offsetLeft) {
return false;
}
return true;
}
function MoveLeft(px) {
var ul = GetList();
if (!ul) {
return false;
}
var li = GetListItem(ul, 0);
if (!li) {
return false;
}
var m = li.style.marginLeft;
var n = 0;
if (m.length != 0) {
n = parseInt(m);
}
n -= px;
li.style.marginLeft = n + "px";
li.style.zoom = "1";
return true;
}
似乎需要超过300毫秒来返回值,而自动收报机假设每10毫秒左移1个像素。
是否有针对此的已知修复程序?
由于
答案 0 :(得分:4)
我同意@samccone的意见,如果GetList()
和GetListItem()
每次都在执行DOM操作,您应该尝试尽可能多地保存对这些调用检索到的元素的引用,并减少DOM操作
然后我可以操纵该变量,希望它不会通过调用offsetLeft与“真实”值不同步。
您只需在变量中存储对DOM元素的引用。由于它是一个参考,它是真正的价值。它是完全相同的对象。 E.g:
var li = ul.getElementsByTagName( "li" )[ index ];
存储对DOM对象的引用。您可以随时从该对象中读取offsetLeft
,而无需执行其他DOM操作(如getElementsByTagName
)来检索对象。
另一方面,以下只会存储该值并且不会保持同步:
var offsetLeft = ul.getElementsByTagName( "li" )[ index ].offsetLeft;
如果offsetLeft
确实是一个瓶颈,那么您是否有可能将其重新编写为少读它?在这种情况下,每次旋出第一个项目时,您可以为新的第一个项目读取offsetLeft
一次,然后在每个调用中将该值减少到MoveLeft()
,直到达到0
(管他呢)? E.g。
function MoveLeft( px ) {
current_offset -= px;
如果你想要更加积极地避免offsetLeft
,也许你可以做一些事情,你读取每个列表项的宽度一次,并且第一项的offsetLeft
一次,然后只是使用这些值来确定何时旋转,而无需再次调用offsetLeft
。
我想我明白了......所以榆树[“foo”]必须是一个全局变量吗?
我认为我只需要使用全局变量而不是每10毫秒调用一次offsetLeft。
您不需要使用全局变量,事实上您应该避免使用它 - 这是糟糕的设计。在不使用全局变量的情况下,您可以采用至少一些好的方法:
您可以将整个程序包装在一个闭包中:
( function () {
var elements = {};
function NeedsRotating() {
...
}
function example() {
// The follow var declaration will block access
// to the outer `elements`
var elements;
}
// Rest of your code here
} )();
elements
的范围限定在包含它的匿名函数中。它不是全局变量,在匿名函数之外不可见。只要您不在内部函数中声明同名变量,任何代码都可以看到它,包括匿名函数中的函数(例如本例中的NeedsRotating()
)。
您可以将所有内容封装在对象中:
( function () {
var ticker = {};
ticker.elements = {};
// Assign a method to a property of `ticker`
ticker.NeedsRotating = function () {
// All methods called on `ticker` can access its
// props (e.g. `elements`) via `this`
var ul = this.elements.list;
var li = this.elements.list_item;
// Example of calling another method on `ticker`
this.do_something();
} ;
// Rest of your code here
// Something like this maybe
ticker.start();
} )();
在这里,我再次将所有内容包装在一个匿名函数中,以便即使ticker
也不是全局变量。
首先,关于setTimeout
,你最好这样做:
t = setTimeout( TickerLoop, i );
而不是:
t = setTimeout("TickerLoop();", i);
在JS中,函数是第一类对象,因此您可以将实际的函数对象作为参数传递给setTimeout
,而不是传递字符串,就像使用eval
一样。
您可能需要考虑setInterval
而不是setTimeout
。
因为在setTimeout中执行的任何代码肯定都不在闭包范围内吗?
事实并非如此。在定义函数时形成闭包。因此,通过setTimeout
调用函数不会干扰函数对闭合变量的访问。这是一个简单的演示片段:
( function () {
var offset = 100;
var whatever = function () {
console.log( offset );
};
setTimeout( whatever, 10 );
} )();
但是, setTimeout
会干扰方法中this
的绑定,如果将所有内容封装在对象中,这将是一个问题。以下内容不起作用:
( function () {
var ticker = {};
ticker.offset = 100;
ticker.whatever = function () {
console.log( this.offset );
};
setTimeout( ticker.whatever, 10 );
} )();
在ticker.whatever
内,this
不会引用ticker
。但是,在这里你可以使用匿名函数来形成一个闭包来解决问题:
setTimeout( function () { ticker.whatever(); }, 10 );
我应该将它存储在类变量即
var ticker.SecondLiOffsetLeft = GetListItem(ul, 1).offsetLeft
中,然后我只需要在旋转列表时再次调用offsetLeft
。我认为这是全局变量的最佳替代方案吗?
关键是:
每次轮播列表时都会访问offsetLeft
。
如果将列表项存储在变量中,则可以访问其offsetLeft
属性,而无需重复执行getElementsByTagName()
之类的DOM操作来获取列表对象。
#2中的变量可以是对象属性,如果将所有内容包装在对象中,或者只是通过其闭包范围可以访问函数的变量。我可能会将它包装在一个物体中。
我更新了“DOM操作”部分,以阐明如果存储对DOM对象的引用,它将是完全相同的对象。您不希望直接存储offsetLeft
,因为这只是存储值而不会保持同步。
但是你决定存储它们(例如对象属性或变量),你应该检索一次所有li
个对象并将它们存储在类似数组的结构中。 E.g。
this.li = ul.getElementsByTagName( "li" );
每次旋转时,都会以某种方式指示当前项目,例如:
this.current_item = ###;
// or
this.li.current = this.li[ ### ];
// Then
this.li[ this.current_item ].offsetLeft
// or
this.li.current.offsetLeft
或者,如果您需要,可以将li
个对象存储在数组中,并为每次轮换执行此操作:
this.li.push( this.li.shift() );
// then
this.li[0].offsetLeft
答案 1 :(得分:1)
如果您不在var li = GetListItem(ul, 1);
然后性能会受到很大影响..这就是你所看到的,因为你每隔10毫秒启动一个新的选择器
你应该将选择器缓存在像
这样的哈希中elms["foo"] = elms["foo"] || selectElm(foo);
elms["foo"].actionHere(...)
答案 2 :(得分:1)
您的代码很慢,因为阅读offsetLeft
会强制浏览器进行重排。回流是使你减速的部分。浏览器通常足够聪明,可以对更改进行排队以减少回流次数。但是,如果您希望在访问offsetLeft
时获得最新值,则会强制浏览器刷新该队列并进行重排以便为您计算正确的值。
如果不知道您要做什么的所有细节,很难知道建议什么来提高性能。 http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/更详细地解释了这个问题,并提供了一些关于减少回流的建议。