MZhou's blog

GA源代码里的小技巧之preview和prerender

作者前段时间在做类似Google Analytics(以下简称GA)的第三方监控脚本。所以对GA的前端代码做过调研,对GA的压缩后代码做了一定程度上的人肉美化。这里美化的是analytics.js的j41版本,本文提到的小技巧也是基于这个版本的js。

preview

Safari浏览器有个Top site功能,它会展示最长访问的几个页面的截图。示例如下:

Safari会去真正的加载解析这几个站点页面,然后截图并保存。当用户点开这些站点时会再次加载解析这些页面。

GA的功能是统计正常用户的访问情况,很明显这种情况已经不是正常的访问了,所以对这种情况做了过滤。凡是是预览(preview)请求都不会执行自己的主要逻辑。示例代码如下:

function isPreviewLoad(win) {
    win = win || window;
    var api = 'navigator';
    return win[api] && win[api].loadPurpose === 'preview';
};

if (isPreviewLoad()) {
    // TODO
}

prerender

W3C标准(WD)中有个prerender特性。如果在指定页面index.html的HTML代码头部中加入如下的meta标签:

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>index.html</title>
<!-- 预渲染 -->
<link rel="prerender" href="./article.html">
</head>
<body>
<a href="./article.html">文章地址</a>
<body>
</html>

那么浏览器在加载index.html的时候,会预先加载渲染article.html页面,但是不展现。当浏览器真正点开页面中article.html页面的地址时,浏览器才会再真正的展现这个页面。不过用户也有可能不再点击article.html的地址,而是直接离开了或是跳去别的页面。目前IE>=11.aspx)、Chrome>=13、Opera>=15都支持了这个特性。

GA为了避免发送无用的统计,也过滤掉了预渲染的情况,在页面真正展示的时候再执行自己的主逻辑。它通过浏览器提供的Page Visibility API来判断当页面处于的状态。如果页面是预渲染,那么页面渲染时document.visibilityState的值为prerender。然后监听visiableChange事件,当页面可见之后开始执行业务主逻辑。

综合previewprerender两种情况,我们可以用如下代码来判断是否需要执行自己的业务主逻辑:

function start(win, main) {
    if (isPreviewLoad(win)) {
        return;
    }

    var doc = win.document;
    var executed = false;
    var isPrerender = function () {
        return doc.visibilityState === 'prerender';
    };
    var cb = function () {
        if (!executed && !isPrerender(win)) {
            executed = true;
            main();
            // 解除事件监听
            off(doc, 'visibilitychange', cb);
        }
    };

    if (isPrerender(win)) {
        // 添加事件监听
        on(doc, 'visibilitychange', cb);
        return;
    }
    main();
}
start(window, function () {
    // TODO 业务主逻辑
});

参考资料:

  1. Safari Top Sites Preview
  2. CanIUse
  3. IE Prerender and prefetch support.aspx)
  4. W3C Resource Hints Spec
  5. Prefetching, preloading, prebrowsing

MZhou's blog - Taste of life.

zmmbreeze / @zhoumm