在此 Codelab 中,您将学习如何使用 Google Analytics(分析)和 User Timings API 衡量您的网站或应用的实际性能,并对其进行优化以改善用户体验。
像 WebPagetest.org 一样是提升性能的理想之选,但真正的网站性能测试始终都是来自实际用户的真实数据。
如果您运营着网站,那么您可能已经在使用 Google Analytics(分析)来衡量流量,以及衡量设备和浏览器的使用情况等。只需额外添加少量代码,您就可以向组合中添加效果指标。
学习内容
- 如何使用 User Timing API 准确有效地衡量性能指标
- 如何将相关数据发送到 Google Analytics(分析)以纳入报告
您需要满足的条件
- 带开发者控制台的浏览器
- 网络服务器(适用于 Chrome),或者使用您自己的网络服务器
- 示例代码
- 文本编辑器
- (可选)Google Analytics(分析)帐号
您打算如何使用本教程?
您如何评价自己在构建网站或应用方面的经验?
您可以将所有示例代码下载到您的计算机...
...或者从命令行克隆 GitHub 代码库。
git clone https://github.com/googlecodelabs/performance-analytics.git
示例代码分为多个子目录,这些子目录对应于此 Codelab 中的每个编号步骤。您可以使用此代码在 Codelab 中轻松跳过,或验证您的实现是否正确。
如果您有权访问差异化计划,则可以使用它来查看确切的变更。
在此 Codelab 中,您将获取一个 HTML 文件,用于加载以下素材资源:
- 网页字体
- 样式表
- 映像
- JavaScript
您需要编写新的代码来衡量每种素材资源类型的关键效果指标。
素材资源效果注意事项
如果您曾阅读过任何有关性能优化的内容,您可能已经知道,每种素材资源类型都有自己的怪异行为,可以通过各种方式影响总体感知效果。
CSS
例如,样式表会禁止在 DOM 中呈现其后的所有元素,这意味着浏览器必须请求、下载并解析样式表,然后才能呈现 DOM 中紧跟其后面的任何内容。因此,通常最好将样式表放在文件的 <head> 中。而且,由于 CSS 的阻塞性,我们通常建议仅将关键 CSS 放置在 <head> 中,然后以异步方式加载非关键 CSS。
JavaScript
另一方面,JavaScript 不会阻止呈现,但会阻止 DOM 的解析和构建。此操作非常有必要,因为 JavaScript 可以修改 DOM,这意味着浏览器每次看到 <script> 标记(不包括异步脚本)时,都必须执行代码,然后才能继续处理下一个标记。如果 <script> 标记引用了外部 JavaScript 文件,则必须先下载并执行代码,然后才能继续。
因此,通常建议您将 JavaScript 紧挨着放在结束 <lt>/body> 标记之前,以便尽快加载大多数 DOM。
网页字体
如果您加载的是网页字体,还可以选择禁止呈现文档,直到字体可以使用为止。在这种情况下,了解用户实际需要多长时间就十分重要。网络字体对您的网站来说加载速度快,但对访问您网站的大多数用户来说速度都很慢。正因如此,衡量和制定实际数据非常重要。
图片
最后,图片确实可以让网站变得生动有趣,但往往也加载时间最长。了解实际意味着什么,并能够发现使用模式与网页加载时间之间的所有相关性,对于了解如何进行优化至关重要。
此 Codelab 中的第一步是在添加任何效果衡量代码之前,先查看演示页面的外观。
要查看演示,请创建一个新文件夹,然后在其中添加名为 index.html 的文件。然后复制以下代码并将其粘贴到 index.html 文件中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Performance Analytics Demo</title>
<!-- Start fonts -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700,400italic" rel="stylesheet">
<!-- End fonts -->
<!-- Start CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<style>
body { font-family: Roboto, sans-serif; margin: 1em; }
img { float: left; height: auto; width: 33.33%; }
.gallery { overflow: hidden; }
</style>
<!-- End CSS -->
</head>
<body>
<div class="container">
<!-- Start images -->
<div class="gallery">
<img src="http://lorempixel.com/380/200/animals/1/">
<img src="http://lorempixel.com/380/200/animals/2/">
<img src="http://lorempixel.com/380/200/animals/3/">
</div>
<!-- End images -->
<h1>Performance Analytics Demo</h1>
<p>Real performance data from real users.</p>
</div>
<!-- Start JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<!-- End JavaScript -->
</body>
</html>
接下来,打开 Chrome 的网络服务器,然后在您刚刚创建的目录中启动一个本地服务器。请务必选中“自动显示 index.html”选项。
现在,您应该可以在浏览器中转到 http://127.0.0.1:8887/ 并查看演示文件了。代码应如下所示:
运行演示页面后,请花点时间查看代码,了解正在加载的各种素材资源类型。在接下来的几个步骤中,您将添加代码以衡量这些资源的加载时间并可由用户与之互动。
如前面的素材资源性能注意事项部分所述,CSS 会在 DOM 中阻止 DOM 元素的呈现,以及在其之后执行的脚本。
您刚刚创建的演示文件包含以下 CSS,它引用了引导加载程序和一些内嵌样式。
<!-- Start CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<style>
body { font-family: Roboto, sans-serif; margin: 1em; }
img { float: left; height: auto; width: 33.33%; }
.gallery { overflow: hidden; }
</style>
<!-- End CSS -->
由于 CSS 会同时阻止 DOM 元素的呈现和脚本的执行,因此可通过在存储当前时间的 CSS 后紧接着添加 <script>
标记来确定 CSS 何时完成拦截。
为此,您可以创建一个变量并为其分配 new Date()
,但借助 User Timings API,有一种更简单的方法:使用 performance.mark
方法。
如需标记 CSS 会在何时阻塞渲染和脚本执行,请在结束 <!-- End CSS -->
注释的前面添加下面这行代码。
<script>performance.mark('css:unblock');</script>
performance.mark
方法会在此精确时间点创建高分辨率时间戳,并将其与传递给该方法的任何名称相关联。在本例中,您将标记命名为“qus;css:unblock”。
performance.mark
方法与 performance.measure
方法搭配使用,该方法用于计算两个标记之间的时差(除了您创建的标记之外,您还可以使用浏览器针对 Navigation Timing API 中的各个点自动设置的标记)。
以下实用函数用于测量并返回您已添加的标记与 Navigation Timing API 创建的 responseEnd 标记之间的时长。
function measureDuration(mark, opt_reference) {
var reference = opt_reference || 'responseEnd';
var name = reference + ':' + mark;
// Clears any existing measurements with the same name.
performance.clearMeasures(name);
// Creates a new measurement from the reference point to the specified mark.
// If more than one mark with this name exists, the most recent one is used.
performance.measure(name, reference, mark);
// Gets the value of the measurement just created.
var measure = performance.getEntriesByName(name)[0];
// Returns the measure duration.
return measure.duration;
}
如需开始使用此实用函数,请在 index.html
文件所在的目录中创建一个名为 perf-analytics.js
的新文件,然后将上述代码复制并粘贴到其中。
现在,此函数已定义,您可以调用函数并传入“css:unblock”标记名称。为了不干扰任何其他资源的加载,您应将这些时间范围的衡量推迟到窗口的加载事件触发之后。
编写好用于调用此代码的函数后,perf-analytics.js
文件应如下所示:
window.onload = function() {
measureCssUnblockTime();
};
/**
* Calculates the time duration between the responseEnd timing event and when
* the CSS stops blocking rendering, then logs that value to the console.
*/
function measureCssUnblockTime() {
console.log('CSS', 'unblock', measureDuration('css:unblock'));
}
/**
* Accepts a mark name and an optional reference point in the navigation timing
* API and returns the time duration between the reference point and the last
* mark (chronologically).
* @param {string} mark The mark name.
* @param {string=} opt_reference An optional reference point from the
* navigation timing API. Defaults to 'responseEnd'.
* @return {number} The time duration
*/
function measureDuration(mark, opt_reference) {
var reference = opt_reference || 'responseEnd';
var name = reference + ':' + mark;
// Clears any existing measurements with the same name.
performance.clearMeasures(name);
// Creates a new measurement from the reference point to the specified mark.
// If more than one mark with this name exists, the most recent one is used.
performance.measure(name, reference, mark);
// Gets the value of the measurement just created.
var measure = performance.getEntriesByName(name)[0];
// Returns the measure duration.
return measure.duration;
}
最后,您需要从 index.html
加载 perf-analytics.js
脚本。为此,请将以下脚本代码添加到主文档中。请务必最后再添加此代码,以免干扰其他资源加载。
<!-- Start performance analytics -->
<script async src="perf-analytics.js"></script>
<!-- End performance analytics -->
完成此步骤后,您的代码就应与此 Codelab 代码库的 01-css
目录中的内容相匹配。
如果您在浏览器中加载页面并打开开发者控制台,应该会看到类似以下输出的内容:
网页字体通常会通过外部样式表加载,如初始演示文件所示:
<!-- Start fonts -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700,400italic" rel="stylesheet">
<!-- End fonts -->
由于此代码是 CSS 文件的 <link>
标记,因此这看起来像确定第 1 步中的字体何时加载并可供使用一样简单,只需立即在 <link>
标记内在 <link>
标记内添加标记即可。
很遗憾,这并非易事。
样式表会阻止 JavaScript 的执行,因为样式表的内容用于构建 CSSOM,并且正在加载的 JavaScript 可能需要访问 CSSOM,所以执行过程必须延迟,直到 CSSOM 完全构建完毕。
问题在于,浏览器在实际下载字体时可能不会构建 CSSOM,这意味着,如果您通过内嵌脚本代码向 DOM 紧跟 font'ssheetsheet <link> 标记添加标记,那么标记很可能会在字体完全加载之前发生。
在浏览器中获取字体加载事件之前,需要使用 JavaScript 来确定字体何时真正处于活动状态并准备好在网页上使用。幸运的是,通过 JavaScript 加载字体也正好是性能提升的原因,因为它不需要向 CSS 文件发送其他阻止请求。
大多数网页字体(包括 Google 字体、typekit 和 font.com 字体)都可以通过 webfont.js 脚本加载,该脚本是由 Google 和 Typekit 联合开发的。
要将主文档更新为使用 webfont.js 加载字体(而不是 <link> 标记),请将代码的字体部分替换为以下代码:
<!-- Start fonts -->
<script>
window.WebFontConfig = {
google: {families: ['Roboto:400,700,400italic']},
timeout: 10000,
active: function() {
performance.mark('fonts:active');
}
};
</script>
<script async src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
<!-- End fonts -->
对于上述代码,请注意以下两点:
- 它会在有效回调中创建“fonts:active”标记,以便您稍后衡量加载字体所需的时间。
- 加载 webfonts.js 的
<script>
标记包含async
属性,因此它不会阻止解析或呈现文档的其余部分(对<link>
标记而言并非如此)。
尽管上述代码会创建“fonts:active”标记,但测量该标记并将其记录到控制台却并不像“css:unblock”标记那么简单。这是因为字体加载目前是异步进行的,因此,如果您尝试在 window.onload
处理程序中测量“fonts:active”标记(就像您测量“css:unblock"”时一样),就很有可能尚未加载字体。
要解决此问题,您可以创建一个在字体加载后解析的 promise。以下函数会为您执行此操作。将其复制并粘贴到 perf-analytics.js 文件中:
/**
* Creates a promise that is resolved once the web fonts are fully load or
* is reject if the fonts fail to load. The resolved callback calculates the
* time duration between the responseEnd timing event and when the web fonts
* are downloaded and active. If an error occurs loading the font, this fact
* is logged to the console.
*/
function measureWebfontPerfAndFailures() {
new Promise(function(resolve, reject) {
// The classes `wf-active` or `wf-inactive` are added to the <html>
// element once the fonts are loaded (or error).
var loaded = /wf-(in)?active/.exec(document.documentElement.className);
var success = loaded && !loaded[1]; // No "in" in the capture group.
// If the fonts are already done loading, resolve immediately.
// Otherwise resolve/reject in the active/inactive callbacks, respectively.
if (loaded) {
success ? resolve() : reject();
}
else {
var originalAciveCallback = WebFontConfig.active;
WebFontConfig.inactive = reject;
WebFontConfig.active = function() {
originalAciveCallback();
resolve();
};
// In case the webfont.js script fails to load, always reject the
// promise after the timeout amount.
setTimeout(reject, WebFontConfig.timeout);
}
})
.then(function() {
console.log('Fonts', 'active', measureDuration('fonts:active'));
})
.catch(function() {
console.error('Error loading web fonts')
});
}
此外,还要更新 window.onload
处理程序以调用此新函数
window.onload = function() {
measureCssUnblockTime();
measureWebfontPerfAndFailures();
};
完成此步骤后,您的代码就应与此 Codelab 代码库的 02-fonts
目录中的内容相匹配。
如果您在浏览器中加载页面并打开开发者控制台,应该会看到类似以下输出的内容:
了解图片何时可见并不像表面上那么简单。您在前面的步骤中已经知道,样式表和同步 <script>
标记可能会阻塞渲染、解析和脚本执行。您可能不知道,这两者都没有屏蔽浏览器的预加载扫描程序。
预加载扫描程序是所有现代浏览器都实施的一种提高性能的方法,即使在那些包含大量屏蔽资源的非性能网站上也是如此。其理念在于,虽然某些素材资源可能会阻止解析、呈现或脚本执行,但它们并不一定要禁止下载。因此,浏览器会在开始构建 DOM 之前扫描 HTML 文件,并查找可立即开始下载的素材资源。
就图片而言,这意味着在将图片添加到 DOM 之前,图片很有可能已经下载完毕。这也意味着下载映像的时间点并不一定具有良好的性能指标。您应该关注的效果指标是图片对用户可见的时间点。
如果在图片添加到 DOM 之前下载图片,则图片在 DOM 中的可见点就是它在 DOM 中的时间点。另一方面,如果在将图像添加到 DOM 之前尚未下载图像,则图像的可见点会在其 onload
处理程序触发时触发。
因此,为了知道图片何时可见,您必须处理这两种情况。
为此,您可以在每个图像的 onload 处理程序以及 DOM 中最后一张图片之后的内嵌 <script> 标记中添加标记。该想法是,最后出现的标记将代表所有图像可见的标记。
若要在加载图像和重新渲染图像时添加标记,请更新 index.html
中的图像代码,如下所示:
<!-- Start images -->
<div class="gallery">
<img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
<img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
<img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>performance.mark('img:visible')</script>
<!-- End images -->
由于特定标记名称的 performance.measure
方法将始终使用最后一个标记(如果它查找具有相同名称的多个标记),因此 perf-analytics.js
文件中的 measureDuration
实用函数可用于实现此目的,而无需进行任何其他修改:
/**
* Calculates the time duration between the responseEnd timing event and when
* all images are loaded and visible on the page, then logs that value to the
* console.
*/
function measureImagesVisibleTime() {
console.log('Images', 'visible', measureDuration('img:visible'));
}
将上述函数添加到 perf-analytics.js
文件中,然后更新 window.onload
处理程序以调用它:
window.onload = function() {
measureCssBlockTime();
measureWebfontPerfAndFailures();
measureImagesVisibleTime();
};
完成此步骤后,您的代码就应与此 Codelab 代码库的 03-images
目录中的内容相匹配。
如果您在浏览器中加载页面并打开开发者控制台,应该会看到类似以下输出的内容:
由于不带 async
属性的 <script>
标记会阻塞 DOM 的解析,直到下载完毕并执行完毕,因此您可以通过在 DOM 中最后一个同步 <script>
之后的内嵌脚本标记中添加一个标记来确定所有脚本都已完成执行的时间点。
请注意,在这种情况下无法使用 onload
处理程序,因为浏览器必须在脚本加载后执行脚本,这需要时间。快速加载但执行缓慢的脚本与慢加载脚本一样糟糕。
如需跟踪主 JavaScript 中所有 JavaScript 的加载和执行时间,请使用以下代码更新 index.html
中的 JavaScript 部分:
<!-- Start JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>performance.mark('js:execute');</script>
<!-- End JavaScript -->
这会在名为 jQuery 和 Bootstrap 插件的脚本下载完毕并立即执行后,添加一个名为“qu:js:execute”的标记。
如需测量需要多长时间,请将以下函数添加到 perf-analytics.js
:
/**
* Calculates the time duration between the responseEnd timing event and when
* all synchronous JavaScript files have been downloaded and executed, then
* logs that value to the console.
*/
function measureJavaSciptExecutionTime() {
console.log('JavaScript', 'execute', measureDuration('js:execute'));
}
然后从 window.onload
处理脚本中调用该函数:
window.onload = function() {
measureCssBlockTime();
measureWebfontPerfAndFailures();
measureImagesVisibleTime();
measureJavaSciptExecutionTime();
};
完成此步骤后,您的代码就应与此 Codelab 代码库的 04-javascript
目录中的内容相匹配。
如果您在浏览器中加载页面并打开开发者控制台,应该会看到类似以下输出的内容:
并非所有浏览器都支持 JavaScript promise 或 User Timing API,如果您在到目前为止不支持上述任一功能的浏览器中运行代码,则会收到错误消息。
为解决此问题,您可以使用特征检测。将以下代码行添加到字体部分之前,且紧邻该部分。下面这行 JavaScript 代码支持 performance.mark
方法,因此必须先向网页添加该方法,然后才能使用该方法:
<!-- Start feature detects -->
<script>window.__perf = window.performance && performance.mark;</script>
<!-- End feature detects -->
接下来,在 index.html
中您调用 performance.mark
的任何位置为功能检测添加前缀。以下示例更新了图像块中的代码,但您还必须确保更新其他部分。
<!-- Start images -->
<div class="gallery">
<img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
<img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
<img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>__perf && performance.mark('img:visible')</script>
<!-- End images -->
现在,window
上已设置 __perf
变量,您还可以在 perf-analytics.js
中使用该变量,以确保不会调用当前浏览器不支持的方法。
必须更新 measureDuration
函数以添加以下条件:
function measureDuration(mark, opt_reference) {
if (window.__perf) {
// ...
}
}
最后,由于并非所有浏览器都支持 JavaScript promise,因此也必须将 measureWebfontPerfAndFailures
函数封装在条件语句中:
function measureWebfontPerfAndFailures() {
if (window.Promise) {
// ...
}
}
现在,您应该可以在任何浏览器中正常运行代码了。
完成此步骤后,您的代码就应与此 Codelab 代码库的 05-feature-detects
目录中的内容相匹配。
此 Codelab 的最后一步是将正在记录到控制台中的数据改为发送到 Google Analytics(分析)。您必须先向网页添加 analytics.js 库和默认跟踪代码段,然后才能向 Google Analytics(分析)发送数据。
将以下代码添加到 index.html
(在主 JavaScript 代码块之后,在 perf-analytics.js
脚本加载之前):
<!-- Start analytics tracking snippet -->
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<script async src="https://www.google-analytics.com/analytics_debug.js"></script>
<!-- End analytics tracking snippet -->
如果您之前向网站添加了 Google Analytics(分析),则必须使用您在 Google Analytics(分析)中创建新媒体资源时获得的跟踪 ID 替换“UA-XXXXX-Y”占位符。
analytics.js 跟踪代码段有以下四大作用:
- 创建用于下载 analytics.js JavaScript 库的异步
<script>
元素。 - 初始化全局函数
ga()
(称为 ga() 命令队列),您可以通过该函数安排相关命令,使之在 analytics.js 库加载完毕可供使用时立即执行。 - 在
ga()
命令队列中添加一条命令,为通过'UA-XXXXX-Y' 参数指定的媒体资源创建新的跟踪器对象。 - 在
ga()
命令队列中添加另一条命令,向 Google Analytics(分析)发送当前网页的网页浏览数据。
虽然仅从网页浏览数据中收集的信息很有用,但并不能体现所有情况。要更好地了解用户在您网站或应用中的体验,您需要向 Google Analytics(分析)发送其他互动数据。
Google Analytics(分析)支持多种类型的互动数据:网页浏览、事件、社交互动、异常以及(最后一次但并非最晚)用户计时。要将用户计时数据发送到 Google Analytics(分析),您可以使用以下命令签名:
ga('send', 'timing', timingCategory, timingVar, timingValue);
其中 timingCategory
是一个可让您将计时命中归类到相应逻辑组的字符串,timingVar
是您要衡量的变量,timingValue
是以毫秒为单位的实际实际时长。
为了了解其实际运行情况,可以按如下方式更新 measureCssUnblockTime
函数中的 console.log
语句:
ga('send', 'timing', 'CSS', 'unblock', measureDuration('css:unblock'));
上述代码在某些情况下可以发挥作用,但要注意以下两个重要陷阱:
- 上一步中的
measureDuration
函数已更新为仅在浏览器支持 User Timings API 时运行,这意味着它有时会返回undefined
。由于没有理由向 Google Analytics(分析)发送未定义的数据(在某些情况下,甚至可能会弄乱您的报告),因此您只应在measureDuration
返回值时发送此计时命中。 - 当
measureDuration
返回值时,它是DOMHighResTimeStamp
,其精度高于毫秒。由于 Google Analytics(分析)中的timingValue
必须是整数,因此您必须对measureDuration
返回的值进行四舍五入。
为了解释这些陷阱,请更新 measureDuration 函数中的 return 语句,以对返回值进行四舍五入:
function measureDuration(mark, opt_reference) {
if (window.__perf) {
// ...
return Math.round(measure.duration);
}
}
并将计时命令更新为仅在相关指标存在值时运行。例如,measureCssUnblockTime
函数应更新为如下内容:
function measureCssUnblockTime() {
var cssUnblockTime = measureDuration('css:unblock');
if (cssUnblockTime) {
ga('send', 'timing', 'CSS', 'unblock', cssUnblockTime);
}
}
您需要对所有其他衡量函数进行类似的更新。完成后,最终的 perf-analytics.js 文件应如下所示:
window.onload = function() {
measureCssUnblockTime();
measureWebfontPerfAndFailures();
measureImagesVisibleTime();
measureJavaSciptExecutionTime();
};
/**
* Calculates the time duration between the responseEnd timing event and when
* the CSS stops blocking rendering, then sends this measurement to Google
* Analytics via a timing hit.
*/
function measureCssUnblockTime() {
var cssUnblockTime = measureDuration('css:unblock');
if (cssUnblockTime) {
ga('send', 'timing', 'CSS', 'unblock', cssUnblockTime);
}
}
/**
* Calculates the time duration between the responseEnd timing event and when
* the web fonts are downloaded and active, then sends this measurement to
* Google Analytics via a timing hit. If an error occurs loading the font, an
* error event is sent to Google Analytics.
*/
function measureWebfontPerfAndFailures() {
if (window.Promise) {
new Promise(function(resolve, reject) {
var loaded = /wf-(in)?active/.exec(document.documentElement.className);
var success = loaded && !loaded[1]; // No "in" in the capture group.
if (loaded) {
success ? resolve() : reject();
}
else {
var originalAciveCallback = WebFontConfig.active;
WebFontConfig.inactive = reject;
WebFontConfig.active = function() {
originalAciveCallback();
resolve();
};
// In case the webfont.js script failed to load.
setTimeout(reject, WebFontConfig.timeout);
}
})
.then(function() {
var fontsActiveTime = measureDuration('fonts:active');
if (fontsActiveTime) {
ga('send', 'timing', 'Fonts', 'active', fontsActiveTime);
}
})
.catch(function() {
ga('send', 'event', 'Fonts', 'error');
});
}
}
/**
* Calculates the time duration between the responseEnd timing event and when
* all images are loaded and visible on the page, then sends this measurement
* to Google Analytics via a timing hit.
*/
function measureImagesVisibleTime() {
var imgVisibleTime = measureDuration('img:visible');
if (imgVisibleTime) {
ga('send', 'timing', 'Images', 'visible', imgVisibleTime);
}
}
/**
* Calculates the time duration between the responseEnd timing event and when
* all synchronous JavaScript files are downloaded and executed, then sends
* this measurement to Google Analytics via a timing hit.
*/
function measureJavaSciptExecutionTime() {
var jsExecuteTime = measureDuration('js:execute');
if (jsExecuteTime) {
ga('send', 'timing', 'JavaScript', 'execute', jsExecuteTime);
}
}
/**
* Accepts a mark name and an optional reference point in the navigation timing
* API and returns the time duration between the reference point and the last
* mark (chronologically). The return value is rounded to the nearest whole
* number to be compatible with Google Analytics.
* @param {string} mark The mark name.
* @param {string=} opt_reference An optional reference point from the
* navigation timing API. Defaults to 'responseEnd'.
* @return {?number} The time duration as an integer or undefined if no
* matching marks can be found.
*/
function measureDuration(mark, opt_reference) {
if (window.__perf) {
var reference = opt_reference || 'responseEnd';
var name = reference + ':' + mark;
// Clears any existing measurements with the same name.
performance.clearMeasures(name);
// Creates a new measurement from the reference point to the specified mark.
// If more than one mark with this name exists, the most recent one is used.
performance.measure(name, reference, mark);
// Gets the value of the measurement just created.
var measure = performance.getEntriesByName(name)[0];
// Returns the measure duration.
return Math.round(measure.duration);
}
}
最终的 index.html 文件应如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Performance Analytics Demo</title>
<!-- Start navigation timing feature detect -->
<script>window.__perf = window.performance && performance.mark;</script>
<!-- End navigation timing feature detect -->
<!-- Start fonts -->
<script>
window.WebFontConfig = {
google: {families: ['Roboto:400,700,400italic']},
timeout: 10000,
active: function() {
__perf && performance.mark('fonts:active');
}
};
</script>
<script async src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
<!-- End fonts -->
<!-- Start CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<style>
body { font-family: Roboto, sans-serif; margin: 1em; }
img { float: left; height: auto; width: 33.33%; }
.gallery { overflow: hidden; }
</style>
<script>__perf && performance.mark('css:unblock');</script>
<!-- End CSS -->
</head>
<body>
<div class="container">
<!-- Start images -->
<div class="gallery">
<img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
<img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
<img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>__perf && performance.mark('img:visible')</script>
<!-- End images -->
<h1>Performance Analytics Demo</h1>
<p>Real performance data from real users.</p>
</div>
<!-- Start JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>__perf && performance.mark('js:execute');</script>
<!-- End JavaScript -->
<!-- Start analytics tracking snippet -->
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<script async src="https://www.google-analytics.com/analytics.js"></script>
<!-- End analytics tracking snippet -->
<!-- Start performance analytics -->
<script async src="perf-analytics.js"></script>
<!-- End performance analytics -->
</body>
</html>
如果您加载此页面并查看“网络”面板中的请求,则会看到类似以下内容的内容:
这非常有用,但将这些数据作为网址编码的请求可能会很麻烦。而且,如果您由于任何原因看不到这些请求,也很难找到失败的位置。
在本地开发时,更好的方法是使用调试版 analytics.js,它会在每次运行 analytics.js 命令时将有用的调试信息记录到控制台。如果您
将 index.html
中的 analytics.js 网址更新为 analytics_debug.js
并打开浏览器控制台,您会看到输出的语句如下所示:
至此您已经了解了如何在此演示页面上实施效果衡量,接下来可以尝试将其添加到您自己的网站中,并向 Google Analytics(分析)发送实际用户数据。
为您收集的数据生成报表
在收集几天的性能数据后,您将能够为这些数据生成报表,从而深入了解实际资源的网站加载速度及其资源实际加载的速度。
要访问 Google Analytics(分析)中的“用户计时”报告,请点击顶部的“报告”标签,然后从边栏导航中选择“行为 > 用户计时” (或者按照说明查看帮助中心中的用户计时报告)。
在 Google Analytics(分析)中加载“用户计时”报告时,您应该能够看到与您发送的数据相对应的时间类别。点击任意一项即可查看相应时间数据的详细可视化图表。下图显示了一个过去 24 小时内 Google Fonts 在实际网站上字体加载时间的示例。
恭喜!您已成功完成了此 Codelab。若想深入了解本文内容,下一部分将就如何在此代码的基础上进行构建提供更多数据洞见。
此 Codelab 中介绍的性能指标对于衡量网站向实际用户加载情况至关重要,但这仅仅是开始。如果您希望更深入地了解效果分析,则接下来只需跟踪更多指标即可。
在此 Codelab 中,您跟踪了与资源提供给用户相关的指标。如果您愿意,可以进一步细分其中的大部分内容。例如,您不仅可以测量 JavaScript 何时开始执行,还可以测量加载何时开始执行、何时开始执行,以及最终何时执行完毕,等等。其中每个指标都可能会发现一个问题,而其中一个问题可能无法发现。
除了更为精细地思考问题外,您还应从整体上思考总体效果分析策略。目标是什么?什么是成功?
当谈到任何类型的分析时,您通常需要先提出一个问题,然后弄清楚如何使用数据来回答这个问题。
例如,请思考下面的问题列表,以及如何利用您在此 Codelab 中学到的知识通过分析来解答这些问题:
- 您的跟踪指标值是随着时间的推移而下降还是提高?
- 通过 Service Worker 或本地存储空间使用离线缓存会如何影响网站的整体性能?
- 您的资源是否以最佳方式加载?即,资源下载时间与可用资源之间是否有很大差距?
- 效果与您要跟踪的其他指标(例如注册率、网站停留时间、购买次数等)之间是否有任何关联?
最后,如果您想了解有关网站效果或 Google Analytics(分析)的详细信息,可参考以下实用资源:
- 可助力您打造高性能网站的工具和信息
https://developers.google.com/speed/ - 供开发者利用 Google Analytics(分析)平台的工具和 API
https://developers.google.com/analytics/ - 在线课程,教您如何使用 Google Analytics(分析)产品本身(通过在 Google Analytics(分析)中工作的人员学习)。
https://analyticsacademy.withgoogle.com/