I just saved potentially hundreds of blocking milliseconds from my Dart app's initial loads.







By moving script tags, and using my new pub transformer, you can help your users, too.

Where does the time go?

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Hugs all around</title>

<link rel="stylesheet" href="styles.css">

</head>

<body>

.......

<!-- not a good practice, do not copy -->

<script type="application/dart" src="hugs.dart"></script>

<script data-pub-inline="packages/browser/dart.js">

</body>

</html>

Location and order matters for scripts and CSS resources. Default projects from Dart Editor can be optimized.

The main HTML loads, with links to CSS, dart.js, and the Dart application. The browser downloads the dart.js file and the CSS file, more or less concurrently. The CSS file must finish downloading until the dart.js script is executed. dart.js checks to see if Dart VM is available. It's probably not. dart.js looks for the Dart application script tag. dart.js replaces the script tag with another script tag that points to the Dart app compiled to JavaScript. The browser then, finally, downloads the actual application.









See this output from DevTools for an illustration:

For my simple app, hosted on the public internet, the app loaded in approximately 1.29 seconds. There are two root causes: the unnecessary download of dart.js, and the blocked execution of dart.js.



The app was blocked for 300ms.

Saving time





Step one, move the script tags into the <head> tag, before the CSS resource.



<head>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Hugs</title>

<script async type="application/dart" src="hugs.dart"></script>

<script async data-pub-inline src="packages/browser/dart.js"></script>

<link rel="stylesheet" href="styles.css">

</head>



Dart apps don't begin until after DOMContentLoaded, so it's safe to start downloading the app as soon as possible (as long as the script tag is marked as async).



Step two, add an async attribute to the script tags. This attribute tells the browser not to block page parsing while the script is downloading and executing.



Step three: I wanted the functionality of dart.js without the extra delay in startup. The solution was to move the script tags, and inline dart.js.Step one, move the script tags into the tag, before the CSS resource.Dart apps don't begin until after DOMContentLoaded, so it's safe to start downloading the app as soon as possible (as long as the script tag is marked as).Step two, add anattribute to the script tags. This attribute tells the browserto block page parsing while the script is downloading and executing.Step three: write a pub transformer to rewrite the HTML, and inline the contents of dart.js. Therefor, script_inliner , my new pub package and transformer, was born!





To install the transformer, add it to your pubspec.yaml dependencies:

dependencies:

script_inliner: ">=0.0.1 <0.1.0"





Then, add script_inliner to the list of transformers in your pubspec.yaml:





transformers: - script_inliner





Then, add a data-pub-inline attribute to script tags that should be inlined. For example:





<script data-pub-inline src="packages/browser/dart.js"></script>





When you run pub build or pub serve, the transformer kicks in and inlines the script.

The results









Here's the same app, with the contents of dart.js inlined:





Inlining dart.js, and moving the script tags, resulted in over 25% faster load times.

Please move your script tags and try the



For the curious, here's what the HTML now looks like:



<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Hugs</title>



<script async type="application/dart" src="pub_transformer_test.dart"></script>

<script data-pub-inline="packages/browser/dart.js">

(function() {

if (navigator.userAgent.indexOf('(Dart)') === -1) {

var scripts = document.getElementsByTagName("script");

var length = scripts.length;

for (var i = 0; i < length; ++i) {

if (scripts[i].type == "application/dart") {

if (scripts[i].src && scripts[i].src != '') {

var script = document.createElement('script');

script.async = true;

script.src = scripts[i].src.replace(/\.dart(?=\?|$)/, '.dart.js');

var parent = scripts[i].parentNode;

document.currentScript = script;

parent.replaceChild(script, scripts[i]);

}

}

}

}

})();

</script>



<link rel="stylesheet" href="style.css">

</head>

......



(Fine print: tests from my home machine, over wifi, hitting static files served by Dropbox. Also note: I've barely tested this transformer, and I haven't tried it with too many different scripts. YMMV.)



Measure and optimize

Use the performance rules that you've learned as a guide, but always use the tools to measure and verify. Performance is a feature, and has direct correlation to customer satisfaction and conversions. Time spent on faster app loads is time well spent.





(Banner from http://www.ayoungertheatre.com/wp-content/uploads/2011/11/pm_web.jpg) Notice how the browser no longer requests dart.js (because the contents were inlined), and the browser downloads the app's script file as soon as any other external resource. After numerous reloads (and clearing the cache), the approximate average time was 916ms.Please move your script tags and try the script_inliner transformer and let me know if it works for you.For the curious, here's what the HTML now looks like:(Fine print: tests from my home machine, over wifi, hitting static files served by Dropbox. Also note: I've barely tested this transformer, and I haven't tried it with too many different scripts. YMMV.)Use the performance rules that you've learned as a guide, but always use the tools to measure and verify. Performance is a feature, and has direct correlation to customer satisfaction and conversions. Time spent on faster app loads is time well spent.