Doris Chen is a senior developer evangelist at Microsoft who focuses on web technologies like JavaScript and HTML5. Her presentation covers optimizing Cordova app performance, including measuring startup cost and memory usage, using CSS for gradients instead of images, animating with translate3d instead of left/top, and handling events through bubbling instead of individual listeners. She provides tips like keeping the DOM simple, batching layout changes, and cleaning up unused objects to prevent memory leaks.
2. Meet Doris Chen| @doristchen
• Senior Technology Evangelist, Microsoft
• Focus on Web technologies, JavaScript, and HTML5
• Blogging at http://blogs.msdn.com/b/dorischen/
• Speaks at numerous international conferences and user groups
including O’Reilly OSCON, Fluent, Dev Nexus, HTML5 Dev
Conference, JavaOne, and worldwide User Groups
• Received her Ph.D. from the University of California at Los
Angeles (UCLA) in computer engineering
3. Agenda
• Performance Overview
• What to measure
• Tools
• Optimization Tips
– Document Object Model (DOM)
• Layout Thrashing
• List Virtualization
– CSS, Images, Animation, JavaScript
– Garage Collection
• Event Bubbling
• Memory Leaks
5. Apache Cordova apps are written in HTML, CSS, and JavaScript
that can also access native device capabilities.
6. Cordova apps are web
applications that run inside a
native application.
This allows you to use HTML,
CSS, and JavaScript to build
your app while still having
access to your device’s
hardware capabilities!
Native Wrapper
<webview>
Your JavaScript App
Cordova Plugin JS API
7. What does it mean for something
to be fast and responsive?
12. Devices
• Compared performance on three nice, but aging devices with
similar hardware profiles:
• Nexus 7 2013 (Android)
• iPad Mini 3 (iOS)
• Lumia 928 (WP)
15. Because Cordova apps run inside a webview, they incur overhead
from both the browser layout and scripting engine. This means you
have a web application running inside a native app. That’s the tax.
19. To access device capabilities, Cordova apps must also
communicate with the native application via asynchronous calls
(XHR).
20. Communication Cost
Items 1 2,000 20,000 200,000
Android 4.4 4 ms 22 ms 157 ms 1121 ms
iOS 8.0 3 ms 45 ms 135 ms 1120 ms
WP 8.1 1 ms 27 ms 139 ms 877 ms
23. What’s Right for me?
Which Tool When to Use
Developer Tools –
CPU Profiler
Identify which JavaScript code is taking the most
time to run
Developer Tools –
Timeline (UI) Profiler
Identify whether JavaScript or rendering
performance may be the bottleneck
JavaScript timing APIs
Measure the duration of specific scenarios with
clear start and end points
Visually timing
Measure scenarios that are hard to time with code,
like startup or navigation, or “time to glass”
loading/update that require precise timing
26. Profiling Windows Phone from Visual Studio
http://blogs.msdn.com/b/visualstudioalm/archive/2013/07/12/performance-and-diagnostics-hub-in-visual-studio-2013.aspx
28. Measuring performance via JavaScript
var start = performance.now();
someCodeToTime();
var end = performance.now();
console.log("someCodeToTime took " + (end-start) + "ms");
Note, Safari on iOS still doesn't support performance.now(), so you'll want to polyfill
33. <ul>
<!-- Two elements -->
<li><a href=“dosomething1.html”>Alpha</a></li>
</ul>
<ul>
<!-- One element = less layout, simpler DOM -->
<li aria-role=“button” onclick=“dosomething(a)”>Alpha</li>
</ul>
Good
Bad
34. Reduce your element count
The more elements you have on the page, the
slower your page will be
35. Layout Thrashing
When you make modifications to certain CSS style properties or
parent DOM elements, the rendering engine will cause the layout
of the entire document to refresh.
Sometimes, this refreshing occurs repeatedly…and noticeably if you
have a lot of UI elements displayed!
37. Bad
for (var i = b.children.length - 1; i >= 0; i--) {
b.children[i].style.left = b.children[i].offsetLeft + "px";
b.children[i].style.top = b.children[i].offsetTop + "px";
}
38. Good
for (var i = b.children.length - 1; i >= 0; i--) {
topPx[i] = b.children[i].offsetTop;
leftPx[i] = b.children[i].offsetLeft;
}
for (var i = b.children.length - 1; i >= 0; i--) {
b.children[i].style.left = leftPx[i] + "px";
b.children[i].style.top = topPx[i] + "px";
}
39. Batch Layout Operations Together
Batch the read/change operations on element
properties in a loop that affects layout
40. Fast List Scrolling
• Displaying a list of data is a common scenario for many apps. To
ensure your data displays smoothly – especially when scrolling,
ensure you fetch only the data you want and render only the
content that is going to be necessary.
• This is commonly known as virtualization.
43. Use virtualization
When working with large amounts of data, only work
with the data you need to display content on the
screen.
(Popular control frameworks like WinJS, Ionic, Onsen UI, Kendo UI
and others help handle this for you)
45. You can specify gradients to blend multiple colors together in a
single region. In HTML and CSS, you have several ways of
specifying them. You can use an image or you can specify a
gradient in CSS itself.
Let’s look at which one is faster!
Gradients
49. • Use Images for Gradients & Shadows
CSS gradients are very convenient, but the browser
is more efficient rendering bitmaps
• Gradients implemented via images result in faster
performance
With the progression of CSS filters and better support
for them, this may change in the future
51. Setting Element Position
• Because handheld devices are CPU bound, you want to defer as
much work to your device’s GPU as you can. This can be easily
ensured by setting the appropriate CSS properties.
54. Use translate3d for Animating Position
When moving elements around, use the transform
property’s translate3d function as opposed to setting
the position property directly.
55. CSS vs. JavaScript
Animations created in CSS use a built-in loop. Animations created
in JavaScript can use a custom loop that you build. Let’s see which
is faster.
56. Paint as much as your users can see
Align timers to display frames
setInterval(animate, 0);
setTimeout(animate, 0);
requestAnimationFrame(animate);
setInterval(animate, 1000 / 60);
setTimeout(animate, 1000 / 60);
MORE WORK LESS WORK
58. Use CSS
For the smoothest animations, use CSS animations or CSS
transitions. If you need to use JavaScript, ensure your animation
loop is defined using the requestAnimationFrame function.
60. The garbage collector is responsible for managing the lifecycle of
the objects in your apps. It keeps track of which ones to keep
around and which ones to destroy to free up memory.
While the garbage collector runs automatically, there are things
you can do to optimize its behavior.
61. Bad
function createElements() {
for (var i = 0; i < 100; ++i) {
var xBtn = document.createElement('button');
xBtn.setAttribute('value', 'AA');
xBtn.addEventListener('click', hi, false);
containerDiv.appendChild(xBtn);
xBtn = null;
}
}
function clearElements() {
containerDiv.innerHTML = "";
}
62. Goodfunction createElements() {
for (var i = 0; i < 100; ++i) {
var xBtn = document.createElement('button');
xBtn.setAttribute('value', 'AA');
xBtn.addEventListener('click', hi, false);
containerDiv.appendChild(xBtn);
xBtn = null;
}
}
function clearElements() {
var els = containerDiv.childNodes;
for (var i = 0; i < els.length; i++) {
els[i].removeEventListener('click', hi, false);
containerDiv.removeChild(els[i]);
}
}
63. Be Either Proactive or Be Patient
Use removeEventListener to remove event references before
deleting elements. If you remove elements without removing your
event listener, the memory footprint will steadily increase until the
next garbage collection.
64. Minimize Unnecessary Event Listeners
If you are listening for events on multiple elements that share the
same ancestor element, don’t assign an event handler to each
element. Instead, just assign one event handler to the ancestor
element.
66. Let’s say you want to listen to a click
event on the elements with the id
value of one, two, three, four, and
five.
Note:
They share a common parent with
the theDude element!
70. Bad
// ONE FUNCTION FOR EACH EVENT LISTENER
for (i = 0; i < 100; i++){
img[i].addEventListener("click", function() {
var clickedItem = e.target.id;
alert("Hello " + clickedItem);
});
}
71. Bad
// ONE FUNCTION FOR EACH EVENT LISTENER
for (i = 0; i < 100; i++){
img[i].addEventListener("click", function clickListener(e) {
var clickedItem = e.target.id;
alert("Hello " + clickedItem);
});
}
75. Take Advantage of Event Bubbling
Having one event as opposed to many events reduces the amount
of memory each UI element takes up. For scenarios where you
have many elements and a common ancestor, use the technique
described here
76. Pay Attention to Memory Leaks
When you are making DOM manipulations, simply removing an
element from the tree doesn’t ensure the element gets garbage
collected. You will need to be more proactive about telling
JavaScript to re-claim the memory.
78. Bad
// el is in the global scope, so it persists with the document
var el;
function doSomething() {
el = document.createElement('div');
parent.appendChild(el);
}
function empty() {
parent.innerHTML = "";
}
80. Good
// el is in the local scope, so expires with the function
function doSomething() {
var el = document.createElement('div');
parent.appendChild(el);
}
function empty() {
parent.innerHTML = "";
}
82. Destroy Unused Objects
When you remove DOM elements from the tree or clear out
variables that store a lot of data, be sure to also clear or destroy
any objects that may be referencing them as well. Otherwise, the
elements will avoid getting garbage collected.
84. 1. Your UI is considered fast when it reacts to user actions in
under 100ms.
2. The less elements you have on your page, the faster and less
memory-intensive your app.
3. Use virtualization to manage the size of your DOM
4. Batch layout operations to avoid duplicate reads/writes on
layout properties
Practical Tips (1/3)
85. 5. Implement gradients & shadows as images rather than CSS
6. Use CSS transform: translate3D for animating position
7. Create fast animations in CSS or by using
requestAnimationFrame in JavaScript
Practical Tips (2/3)
86. 8. Use removeEventListener to remove event references from
elements – even if the element is about to be deleted.
9. Reduce the amount of event listeners in your app by relying on
event bubbling
10. Destroy unused elements and objects to save memory.
Practical Tips (3/3)