window.onload后发出的GET请求非常慢,阻塞页面滚动 - 性能分析
我有一个插入到许多网页上的小部件。它由一些 JavaScript 组成,从我的服务器加载 HTML 文档(作为 JSONP),然后将其插入到动态创建的
我使用 Clicky 进行分析/跟踪来测量我的小部件的主页收到的浏览量。最近,我需要更进一步,跟踪小部件本身的实际视图数量。此数据的目的是更准确地解释小部件在生成点击时的性能 - 也就是说,如果访问者没有向下滚动主页足够远以首先看到我的小部件,我就无法激发了点击率。
为了实现这种跟踪,我编写了一个订阅浏览器的“onscroll”事件的函数;基本上,每次调用它时,它都会将主页文档顶部和小部件顶部之间的距离与视口从主页顶部向下滚动的距离加上视口的高度和一半进行比较小部件的高度。当后者超过前者时,可以假定该小部件在浏览器视口中是半可见的。
当该函数确定这种情况已经发生时(小部件需要在视口中保留 2 秒或更长时间才能计数),它会向 Clicky 记录一个“操作”,即通知分析软件这种情况已经发生。这是通过调用从 Clicky 的服务器加载“图像”的预定义函数来完成的 - 基本上是一种使用跨域 GET 请求来传达某些跟踪数据的方法。
问题是该请求需要时间(平均一秒多一点)才能完成,并且在此期间,浏览器窗口无法滚动。这对我来说是一场精彩的表演。轻微的延迟(最好是半秒以下)是可以接受的,但接近一秒的延迟是行不通的。
我已尽力分析各种性能工具生成的数据(Firebug 的 Net Panel、Google Page Speed),但我无法解释发生了什么。
我将非常感谢任何能够对正在发生的事情提供一些见解的人,或者更好的是,分享一个可能的解决方案来减少或消除阻塞的浏览器滚动。完成请求的时间对我来说并不重要,但滚动条“卡住”的时间量至关重要。例如,有没有办法在不中断浏览器滚动条功能的情况下向 Clicky 发出此请求?
作为代码的概念验证,我创建了一个原型,可以在此处查看:
http://troy.onespot.com/static/3128/prototype.html
当您向下滚动页面直到灰色框中间进入视口2秒或更长时间时,出现“小部件视图”的指示符屏幕右上角将显示“已登录。
(我只测试过此代码在 Firefox 3.0 或更高版本中工作 - 事实上,除了可能的 Safari 之外,它不太可能在其他地方工作,因为它不考虑尺寸属性的跨浏览器差异。)
另外,这里是 Google 页面速度工具在此记录期间输出的屏幕截图:
http://img.skitch.com /20100121-t6bt1wauaar2drg1xdmwk9g4sb.png
为了生成这个,我在将灰色框放入视口时不断滚动/抖动页面。可以看到,由“onscroll”事件触发的函数在输出顶部以黑色虚线的形式重复工作。正如您所看到的,一旦发生 Clicky 记录(黑色虚线中的大间隙),大约 1.2 秒就会过去,此时无法滚动。我不知道该时期后半段的空白期间发生了什么,也不太明白为什么整个时期都阻止滚动。
Firebug 的网络面板显示了较短的经过时间(尽管主观上仍然感觉像是一秒或更长):
http://img.skitch.com/20100121-pwf1ifngffsnqm8qekmm8wp9mt.png
在这种情况下,绝大多数时间(544ms)花费在“阻塞”阶段,这对我来说没有意义;我的理解是,只有当请求位于队列中时才会遇到此阶段,因为已经发出了每个主机名的最大请求数。
任何想法、建议或其他见解将不胜感激。谢谢!
I have a widget that is inserted on numerous Web pages. It's composed of some JavaScript that loads an HTML document from my server (as JSONP) which is then inserted into a dynamically created <iframe> on the page where the widget is deployed.
I use Clicky for analytics/tracking to measure the number of pageviews that my widget's host page receives. Recently, I needed to go a bit further, to track the number of actual views of the widget itself. The purpose of this data is to more accurately interpret the performance of the widget at generating clickthroughs - that is, if a visitor doesn't scroll down the host page far enough to see my widget in the first place, there's no way that I could have inspired a clickthrough.
To achieve this tracking, I wrote a function that subscribes to the browser's "onscroll" event; basically, each time it's called, it compares the distance between the top of the host page document and the top of the widget, to the distance that the viewport is scrolled down from the top of the host page plus the height of the viewport and half the height of the widget. When the latter exceeds the former, the widget can be assumed to be halfway visible in the browser viewport.
When the function determines that this has happened (the widget needs to remain in the viewport for 2 seconds or more to count), it logs an "action" to Clicky, i.e., informs the analytics software that this has happened. This is done by calling a predefined function that loads an "image" from Clicky's server - basically a way to use a cross-domain GET request to communicate some tracking data.
The problem is that this request takes time - on average, a little over a second - to complete, and during that time, the browser window can't be scrolled. This is a showstopper for me. A slight delay - ideally well under a half second - is acceptable, but nothing approaching a second will work.
I've done my best to analyze the data that various performance tools generate (Firebug's Net Panel, Google Page Speed), but I'm at a loss to explain what is happening.
I would be extremely grateful to anyone who can provide some insight into what is happening, or even better yet, share a possible solution(s) to reduce or eliminate the blocked browser scrolling. The time to fulfill the request is unimportant to me, but the amount of time that the scrollbar is "stuck" is critical. For example, is there a way to make this request to Clicky without interrupting the browser's scrollbar functionality?
As a proof-of-concept of my code, I had created a prototype, viewable here:
http://troy.onespot.com/static/3128/prototype.html
When you scroll the page down until the middle of the gray box enters the viewport for 2 seconds or more, an indicator that a "widget view" has been logged will appear on the top-right of the screen.
(I've only tested this code to work in Firefox 3.0 or more recent versions - in fact, aside from possibly Safari, it's unlikely to work elsewhere, as it doesn't honor cross-browser differences in dimension properties.)
Also, here is a screenshot of Google's Page Speed tool's output during this logging:
http://img.skitch.com/20100121-t6bt1wauaar2drg1xdmwk9g4sb.png
To generate this, I scrolled/jiggled the page constantly as I eased the gray box into the viewport. The function fired by the "onscroll" event can be seen working repeatedly as a broken black line across the top of the output. As you can see, as soon as the Clicky logging happens (the large gap in the broken black line), approximately 1.2 seconds elapse where scrolling is not possible. I have no idea what is happening during the empty span in the latter half of that period, nor do I really understand why the entire period prevents scrolling.
Firebug's Net Panel shows a shorter period of elapsed time (though it still feels like a second or more, subjectively):
http://img.skitch.com/20100121-pwf1ifngffsnqm8qekmm8wp9mt.png
In this case, the vast majority of time (544ms) is spent in the "Blocking" stage, which makes no sense to me; my understanding was that this stage is only encountered when the request is in a queue because the maximum number of requests per hostname are already being made.
Any ideas, suggestions, or other insight would be very much appreciated. Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在 clicky_custom 配置对象中将计时器设置为 1 而不是 0。他们的代码中有一个错误,如果计时器为 0,则等待 500 毫秒。
我使用 Firebug 的配置文件发现了这一点,并且它们的 init 函数花费了一些时间。代码类似于
timer = config.timer|500
。 config.timer 将计算为 false,因此返回 500。Set the timer to 1 and not 0 in the clicky_custom config object. There is a bug in their code that says that if the timer is 0 then wait 500ms.
I found this out using Firebug's profile and their init function was taking a the time. The code was something like
timer = config.timer|500
. config.timer will evaluate to false so 500 was returned.