This repository was archived by the owner on Dec 15, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathindex.html
More file actions
175 lines (122 loc) · 10.9 KB
/
Copy pathindex.html
File metadata and controls
175 lines (122 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link href='https://fonts.googleapis.com/css?family=Architects+Daughter' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="stylesheets/stylesheet.css" media="screen" />
<link rel="stylesheet" type="text/css" href="stylesheets/pygment_trac.css" media="screen" />
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print" />
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<title>AngularJS - Perceived Performance by mendhak</title>
</head>
<body>
<header>
<div class="inner">
<h1>AngularJS - Perceived Performance</h1>
<h2>Directives for measuring and reporting perceived page performance</h2>
<a href="https://github.com/mendhak/angular-performance" class="button"><small>View project on</small>GitHub</a>
</div>
</header>
<div id="content-wrapper">
<div class="inner clearfix">
<section id="main-content">
<h2>
<a name="page-load-vs-perceived-page-load" class="anchor" href="#page-load-vs-perceived-page-load"><span class="octicon octicon-link"></span></a>Page Load vs Perceived Page Load</h2>
<p>In a traditional page, measuring the page performance is quite easy; a request is made, the server responds with some HTML and the browser renders it. Done.</p>
<p><img src="http://farm3.staticflickr.com/2852/9727108341_c6081f9fb3_o.png" alt="Traditional"></p>
<p>A lot of the rendering logic is taken care of as part of the server processing and so looking at <code>Window Load</code> and <code>DOMContentReady</code> are good indicators of page performance.</p>
<p>In a Single Page Application, things get trickier. The <code>Window Load</code> is only the beginning - that's when the JavaScript has been delivered to the browser, at which point the client-side logic - all the real work - kicks in and begins rendering the page, making API calls and setting up listeners, events, etc.</p>
<p><img src="http://farm8.staticflickr.com/7393/9727108327_91103f0d03_o.png" alt="SPA"></p>
<p>The DOM is then continuously manipulated as part of user interaction or monitoring, polling and other events. As you can see, the traditional definition of a page being 'done' doesn't apply here.</p>
<p>The <em>perceived</em> page performance is how long the user thinks the major elements of the page took to load. By definition it is highly subjective - some users may think that the page is loaded just because the initial furniture appears. But for most users this will be the parts of the page they consider most important. </p>
<p>Taking GMail as an example, most users will consider the page ready when the list of emails appear. Whether or not the social tabs, filters, navigation or GTalk appears is less important. </p>
<p><img src="http://farm6.staticflickr.com/5520/9756473461_815bba6b5b_o.png" alt="gmail"></p>
<p>Similarly, on a news website, the title and body of the news article matter the most. Related articles and featured stories aren't that important, but top stories may matter.</p>
<p><img src="http://farm6.staticflickr.com/5329/9756789024_f61b0d57f4_o.png" alt="bbc"></p>
<p>The images above are just examples with arbitrarily assigned regions of importance. The point here is, the definition of page done has to be defined on a per-case basis. The most common definition is usually something like <em>"The page is done when this particular div is filled with content"</em> - indicating that the page loaded, an API call was made and the contents were rendered. On a heavier page, this would be when three or four divs have all been filled with content. You could even choose to ignore certain parts of the page as being less important.</p>
<h2>
<a name="so-how-do-we-measure-perceived-page-performance" class="anchor" href="#so-how-do-we-measure-perceived-page-performance"><span class="octicon octicon-link"></span></a>So how do we measure perceived page performance?</h2>
<p>The <em>perceived</em> page load is when all of the important dynamic parts of the page have been filled. This requires the developers to agree upon what the most important parts are, and to programmatically indicate when the specific portions are done. It's an inexact science and the results will vary from user to user due to machine specs, network latency and other environmental factors, but you get a good idea of the timings involved and what users are actually experiencing. </p>
<p>Because this is a client side operation, a few components are required:</p>
<ol>
<li>An indicator placed on various parts of the page to watch that specific portion of the page (eg. article body, top articles, but not header or featured stories).</li>
<li>A listener which waits to be informed by all of the indicators; internally the listener can set up various timers as necessary.</li>
<li>
<p>A beacon which the listener can send the aggregate information to once it is satisfied that all of the indicators have reported to it. This beacon usually takes the form of an empty image, with timings passed in the querystring.</p>
<pre><code>/beacon.png?content=3913&name=ArticleView&initial=1011
</code></pre>
<p>The above means it took the ArticleView page 1011 milliseconds for its <em>initial</em> load and 3913 milliseconds to load the actual <em>content</em> (the perceived load time).</p>
</li>
<li><p>The beacon requests will be stored in your web server logs, and a log parsing application (eg. logster) can retrospectively process it, grab the information and store it your aggregating service (eg. graphite).</p></li>
</ol><p><img src="http://farm8.staticflickr.com/7417/9758863125_b186c911d3_o.png" alt="components"></p>
<h2>
<a name="using-the-performance-directives" class="anchor" href="#using-the-performance-directives"><span class="octicon octicon-link"></span></a>Using the performance directives</h2>
<p>The listener shown above is the <code>performance</code> directive. Place this attribute at the beginning of your angular view. </p>
<pre><code><div performance="PageName" performance-beacon="/sample/img/beacon.png">
</code></pre>
<p>The <code>performance-beacon</code> indicates where the HTTP request should go when perceived page load is complete.</p>
<p>The watchers above are the <code>performance-loaded</code> directives. Place these attributes anywhere within the view and set its value to an object on the <code>$scope</code>. For example, you can do this</p>
<pre><code>performance-loaded="ProductsFromAPI"
</code></pre>
<p>This directive will watch the <code>$scope.ProductsFromAPI</code> object and mark loading as done when this object contains a value. You can control this further by using an object just for this directive:</p>
<pre><code>performance-loaded="Loaded"
</code></pre>
<p>And in your controller, only set <code>$scope.Loaded = true</code> when you feel that all the processing is complete. This is useful when your controller makes multiple API calls and you need to wait for all of them to complete before indicating that loading is complete.</p>
<p>Ensure that the <code>performance loaded</code> directives sit within the scope of the <code>performance</code> directive. In other words, the <code>performance-loaded</code> directives should be in the same controller as <code>performance</code> or in a 'sub-controller' inside it. </p>
<p><strong>Correct:</strong></p>
<pre><code><div ng-controller="MyController" performance="PageName">
<div performance-loaded="ProductsFromAPI">
</div>
</code></pre>
<p><strong>Correct:</strong></p>
<pre><code><div ng-controller="MyController" performance="PageName">
<div ng-controller="SomeOtherController" performance-loaded="ProductsFromAPI">
</div>
</code></pre>
<p><strong>Incorrect:</strong></p>
<pre><code><div ng-controller="MyController" performance="PageName">
....
</div>
<div performance-loaded="ProductsFromAPI">
</code></pre>
<p><strong>Incorrect:</strong></p>
<pre><code><div ng-controller="MyController" performance="PageName">
....
</div>
<div ng-controller="SomeOtherController" performance-loaded="ProductsFromAPI">
</code></pre>
<h2>
<a name="democode" class="anchor" href="#democode"><span class="octicon octicon-link"></span></a>Demo/Code</h2>
<p>See <a href="http://code.mendhak.com/angular-performance/sample/">this page</a> for a demo. Be sure to open your networks tab or Fiddler to see the beacon request.</p>
<p><img src="http://farm8.staticflickr.com/7432/9759419411_4bddff429b_o.png" alt="network tab"></p>
<p>Look at <a href="https://github.com/mendhak/angular-performance/blob/master/sample/index.html">index.html</a> and <a href="https://github.com/mendhak/angular-performance/blob/master/sample/js/controllers.js">controllers.js</a> to see how it's done.</p>
<p>You can use <a href="https://raw.github.com/mendhak/angular-performance/master/src/angular-performance.js">angular-performance.js</a> or its <a href="https://raw.github.com/mendhak/angular-performance/master/build/angular-performance.min.js">minified version</a>.</p>
<h2>Other methods</h2>
<p>Understandably, this may not always be the best approach for you. Projects differ in structure as well as the benefit of effort. You may find that simply using a stopwatch and visually sighting the page is a good enough approach.
It sounds crude and unscientific, but can still be considered a legitimate indicator of what users are experiencing. The best approach here is to spin up a few cloud instances in different geographies and navigate to the site several times, taking the average.
It's manual and it works. </p>
<p>Another possible avenue to explore is the upcoming <a href="http://www.w3.org/TR/user-timing/">User Timing Marks</a> specified in the W3C draft. This works by having your code emit marks</p>
<pre><code>performance.mark("Loaded product detail");</code></pre>
<p>And having a listener such as WebPageTest record them. This allows for automation and indication as well as recording of important points of the page's lifecycle.</p>
<h2>
<a name="license" class="anchor" href="#license"><span class="octicon octicon-link"></span></a>License</h2>
<p><a href="https://github.com/mendhak/angular-performance/blob/master/LICENSE">MIT License</a></p>
</section>
<aside id="sidebar">
<a href="https://raw.github.com/mendhak/angular-performance/master/build/angular-performance.min.js" class="button">
<small>Download</small>
.js file
</a>
<a href="http://code.mendhak.com/angular-performance/sample/" class="demo">
<small>View</small>
Demo
</a>
</aside>
</div>
</div>
</body>
</html>