Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何自动获取首屏时间(成熟版) #102

Open
hoperyy opened this issue Mar 5, 2018 · 0 comments
Open

如何自动获取首屏时间(成熟版) #102

hoperyy opened this issue Mar 5, 2018 · 0 comments

Comments

@hoperyy
Copy link
Owner

hoperyy commented Mar 5, 2018

如何自动获取首屏时间

背景

在前端性能数据的获取方法上,现在业内大多使用手动埋点的方式,即在代码中,人工判断首屏完成的位置,并在该处添加首屏记录的代码,类似:firstscreen.report() 这样。

这样做的简单省事,但缺点也很明显:

  • 和业务代码混用

    通用的监控需求混入了业务代码中

  • 覆盖不完整

    需要页面开发者自觉手动添加埋点代码,在业务中埋点覆盖率不一定能达到 100%

  • 准确性不一定高

    由于需要开发者自行判断统计脚本放置的位置,就会存在一些不准确的情况,因为每个人对首屏的理解不同

基于上面的分析,我们近期尝试了一些方案,试图将首屏时间计算自动化,节省人力、并提高准确性。

定义

对首屏时间的定义,每个公司可能会有所不同,在本文中,首屏时间指的是:

  • 如果页面首屏有图片

    首屏时间 = 首屏图片全部加载完毕的时刻 - window.performance.timing.navigationStart
    
  • 如果页面首屏没有图片

    首屏时间 = 页面处于稳定状态前最后一次 dom 变化的时刻 - window.performance.timing.navigationStart
    

实现原理

总体思路为:

  • 从页面加载开始,按照一定的间隔打点,不断记录各个时刻下页面首屏图片列表和其他信息

    问题:按照怎样的间隔打点?

  • 找出页面首屏处于稳定状态的时刻 T1(到这个时刻为止,页面首屏可能已经稳定了一段时间)

    问题:如何找出这个 T1?

  • T1 时刻是页面首屏稳定的时刻(可能已经持续了一段时间),然后向前倒推,哪个时刻最先和稳定时刻的图片数量一致, 定义这个时刻为 T2

  • 统计 T2 时刻的所有图片加载完成时间 T3

  • T3 即为首屏完成的时刻,进行上报

下面,一个个解决上文中提到的问题:

  • 问题:如何找出首屏处于稳定状态的时刻 T1?

    我们将页面从加载到渲染分为两大阶段:1. 获取数据;2. 数据获取完毕,渲染页面。

    这个逻辑符合绝大部分的页面逻辑:先获取数据,再渲染页面。

    解决方案:

    1. 通过 AOP 切面方式监听 XHR 的 send 对象,抓取页面中的第一个 XHR 请求,以第一个 XHR 请求发出的时刻为起点,统计在 1000ms 以内所有发出的请求到数组 Request 中。

      我们认为可能影响首屏的请求在 [第一个 xhr 请求发出的时刻,第一个 xhr 请求发出的时刻 + 1000ms] 的时间段内均已发出。

    2. 针对串联型的请求(即下一个请求依赖上一个请求的返回数据),同时统计每个请求返回后,500ms 以内新发出的请求到数组 Request 中。

      有些页面的数据请求方式是串行的,可能经过两个串联的请求后首屏的数据才能加载。

      影响首屏的请求可能也会以这样的形式发出。

    3. 数组 Request 中统计到的请求,基本包含了所有影响首屏的数据请求,同时也包含了部分不影响首屏的数据请求。

    4. 针对上述统计到的请求,找到所有数据返回的时刻 T1,然后,T1 = T1 + 300ms,保证页面接收数据后渲染完毕(300ms 用于一次渲染足够了)。

    5. 此时的 T1 时刻,页面首屏被认为处于稳定状态。

  • 问题:按照怎样的间隔打点?

    • MutationObserver

      大家都知道 MutationObserver 对象用于捕捉页面 dom 变化,因此在脚本中,我们使用了 MutationObserver 监听 dom 变化,并在每次 dom 变化时触发一次打点(统计该时刻首屏图片信息)

    • setInterval

      setInterval 也能实现定时打点

    • MutationObserver 和 setInterval 组合

      但 MutationObserver 回调函数的触发时机开发者并不可控,有几种情况:

      • 两次回调之间可能距离几百毫秒甚至 1秒多,导致统计误差较大
      • 某些情况下,dom 不再变化,但页面元素中,imgsrc 发生了变化或元素的 background-image 发生了变化,并不会触发在 MutationObserver 的回调,导致统计失误

      因此,我们现在的方案是结合 MutationObserver 和 setInterval,在 MutationObserver 回调的间歇,启动 setInterval,保证页面加载过程中打点间隔不会过长,提高统计准确率。

统计误差

即使使用了上述复杂的打点与判断,误差仍然存在,那么,误差到底在哪里?

如下图所示:

不稳定状态(1 images)   稳定状态2(2 images)      稳定状态1(2 images)
    |                        |                       |
    |________________________|_______________________|
    t1                       t2                      t3

按照上面的理论,我们会取 t2 时刻为可以统计首屏的时刻,两张图片加载完成的时刻即为首屏完成的时刻。

t2t1 时刻差了 1 张图片。

按照我们的理论,首屏完成时间一定在 t2 之后的某个时刻 t2.n

而实际相差的那张图片,什么时候加载完成的,我们不得而知,可能在 t2 前已经加载完毕了,也可能已经发出请求,但还没加载完毕。

误差就在这里,它总会存在。

但我们需要统计的是在误差可以接受范围内的首屏数据,根据在公司业务实践的反馈来看,数据可靠性很高。

Talk is cheap, show me the code

我也开源了这个小工具:

github: https://github.com/hoperyy/auto-compute-first-screen-time

npm: https://www.npmjs.com/package/auto-compute-first-screen-time

欢迎小伙伴们使用,吐槽,改进。

@hoperyy hoperyy closed this as completed Apr 29, 2018
@hoperyy hoperyy reopened this May 29, 2018
@hoperyy hoperyy closed this as completed May 29, 2018
@hoperyy hoperyy reopened this May 29, 2018
@hoperyy hoperyy changed the title 如何自动获取首屏时间 如何自动获取首屏时间(成熟版) May 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant