Angular.js and Google Pagespeed Insights: render-blocking JavaScript and CSS in above-the-fold content

8,277 views
Skip to first unread message

tomw

unread,
Feb 15, 2015, 5:51:05 AM2/15/15
to ang...@googlegroups.com
Hi, 

recently I was just testing a new website build with angular using  Google Pagespeed Insights. Taking care of all the image compression, caching and sizing issues beforehand the analysis left me with a score of 87/100 - with the remaining issue to fix:

Eliminate render-blocking JavaScript and CSS in above-the-fold content


Now, according to the angular.js docs it is recommended to :
 
Place the script tag at the bottom of the page. Placing script tags at the end of the page improves app load time because the HTML loading is not blocked by loading of the angular.js script.


So that's exactly what I did, but still I'm getting these  disastrous results. What am I missing here? 

See the index.html below, vendor.js contains all the angular modules:

 
<!DOCTYPE html>
<html lang="en" ng-app="app">
 
<head>
   
<base href="/">
   
<meta charset="utf-8">
   
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
   
<meta name="viewport" content="width=device-width" initial-scale="1.0">
   
<link rel="stylesheet" href="/css/app.css">
   
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css">
   
<link href="https://fonts.googleapis.com/css?family=Roboto+Condensed" rel="stylesheet" type="text/css">
   
<!--if lte IE 7 script(src='http://cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js')-->
   
<!--if lte IE 8 script(src='//html5shiv.googlecode.com/svn/trunk/html5.js')-->
 
</head>
 
<body>
   
<div ng-controller="AppCtrl as app" class="app">
     
<div id="scrollcontainer" class="app-scroll-container">
       
<div ng-include="'/partials/menu.html'"></div>
       
<div ui-view></div>
       
<div ng-include="'/partials/footer.html'"></div>
     
</div>
   
</div>
   
<script src="/js/vendor.js"></script>
   
<script src="/js/partials.js"></script>
   
<script src="/js/app.js"></script>
 
</body>
</html>

Sander Elias

unread,
Feb 16, 2015, 5:45:08 AM2/16/15
to ang...@googlegroups.com

Hi Tomw,

You can use the defer attribute on your script tags. But then again, your app is not showing anything to the user, until angular kicks in (and is largely done). You should show something to the user, even if everything else on your page fails to load.
adding this right below the body tag will help with your pagespeed:

<div ng-if="false">
   <h1>Loading the application, please hold on</h1>
   <div class='aCssSpinner'</div>
</div>

There are some more things that can be done, but this should put you on the right track.
Regards
Sander

tomw

unread,
Feb 16, 2015, 4:51:49 PM2/16/15
to ang...@googlegroups.com
Hi Sander, 

thanks, clever idea to use ng-if as a switch for a kind of angular-fallback. 
BTW, I have been checking quite a few popular sites regarding their pagespeed score - and discovered, that my 87/100 is a rather outstanding result...

best, tomw 

Sander Elias

unread,
Feb 17, 2015, 2:18:25 AM2/17/15
to ang...@googlegroups.com
Hi Tomw,

Yes, that's the sad truth at the moment. It takes a little effort to optimize a app, and it's not always paying off. However, If I build something public facing, it will show something to the end-user, with just loading index.html. 
Don't let the fact that an 87/100 score sets you above the average withhold you from going the extra mile for customer satisfaction.

Regards
Sander 

tomw

unread,
Feb 17, 2015, 7:00:00 AM2/17/15
to ang...@googlegroups.com
Hi Sander, 

I could not agree more...

best, tomw

Reydi Sutandang

unread,
Apr 2, 2015, 12:15:28 PM4/2/15
to ang...@googlegroups.com
SUPER awesome! thanks for the super trick!

By the way, how do you load the aCssSpineer style super fast too? I have a huge minified css file and it has to wait for that file to be loaded so i could use the aCssSpinner. The rendered text (which i styled too) and the aCssSpinner still took half of the loading time to show up.

Thanks in advance!

Sander Elias

unread,
Apr 3, 2015, 5:14:46 AM4/3/15
to ang...@googlegroups.com
Reydi,

Just look for a small css-spinner and put that inside a style tag in the head. I have done just that a couple of times.
Then use a tool to dynamically load your css, and 'flip the switch' after it's done.

Regards
Sander

Craig McMurray

unread,
Apr 17, 2015, 10:24:18 PM4/17/15
to ang...@googlegroups.com
Putting "critical CSS" in the <head> is definitely great for quickly rendering above the fold content in the initial HTTP request.  Just be careful about "flipping the switch", if done incorrectly it will result in the great FOUC which is rather unsavory

Donald Silveira

unread,
Sep 3, 2015, 10:47:00 AM9/3/15
to AngularJS
Hello Craig.

I'm with a similar problem.
In my application I'm using SASS/Gulp for concat and minify my CSS.

Example how I structured:

<link rel="stylesheet" href="assets/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/css/styles.min.css"> // my SASS files compressed

On this case, what you consider "Critical CSS"?


Craig McMurray

unread,
Sep 3, 2015, 12:11:15 PM9/3/15
to AngularJS
"Critical CSS" is generally anything needed to render any Elements that appear "above the fold".  It allows the browser to start rendering those Elements after the initial request instead of making additional (blocking) request(s) for your CSS.  You mentioned you are using SASS, and then include a separate Bootstrap stylesheet.  You could take your code a step further even and use the Bootstrap SASS project and only import the necessary components you need at the beginning of your SCSS file.  Doing so will 1) reduce the number of requests for external resources and 2) reduce the overall size of CSS by only importing what you need instead of just including every single Bootstrap component.  For example, I use Font Awesome so I don't import glyphicons in my build, and I don't use popovers or tooltips so those aren't imported either. I'm actually only using 20 of the 39 components that are included with the Bootstrap SASS project so it cuts down on my overall file size significantly.  If you are able to successfully cherry pick the "Critical CSS" into the head, you could then use the loadCSS library build by the Filament Group and asynchronously load to bundled CSS which would prevent it from being a blocking resource altogether.  Alternatively, you could use a spinner/pre-loader as mentioned above and then still use the loadCSS library to achieve a similar effect.

Donald Silveira

unread,
Sep 3, 2015, 5:09:01 PM9/3/15
to AngularJS
I never really stopped to think about using only the necessary modules of Bootstrap without loaded all unnecessarily.
I'll try it and include other components like Font Awesome SASS in my builder. Because Im loading font-awesome in separate file too.

The library loadCSS was all I needed, as well the solution of the spinner/pre-loader.

Thanks Craig by cleaned my mind.

Craig McMurray

unread,
Sep 3, 2015, 5:25:45 PM9/3/15
to AngularJS
You can also get a SASS project for Font Awesome too and import it at the beginning of your SCSS with your Bootstrap.  Here is what the beginning of my SCSS looks like: 
/*************** IMPORT STYLES ***************/
//Twitter Bootstrap
@import 'bootstrap';
//Font Awesome Icons
@import '../../bower_components/font-awesome-sass/assets/stylesheets/font-awesome';
//Bourbon
@import '../../bower_components/bourbon/app/assets/stylesheets/bourbon';
//Defined Variables
@import 'vars';
//Defined Animations
@import 'animations';




And then in my 'bootstrap' file, I import only the necessary modules: 
// Core variables and mixins
@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap/variables";
@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap/mixins";

// Reset and dependencies
@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap/normalize";
//@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap/print";
//@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap/glyphicons";

// Core CSS
@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap/scaffolding";
@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap/type";
//@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap/code";
@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap/grid";
@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap/tables";
//*****REST OF IMPORTS LEFT OUT FOR EXAMPLE PURPOSES******//

Donald Silveira

unread,
Sep 3, 2015, 7:26:34 PM9/3/15
to AngularJS
Very nice, I'll structure my project like this.

Thank you again!!

Sander Elias

unread,
Sep 3, 2015, 11:14:22 PM9/3/15
to AngularJS
Hi Donald, Craig.

A while ago I wrote a blog post, explaining how to lazy-load CSS using a few lines of angular. Perhaps this is of some help to you?

Regards
Sander

Donald Silveira

unread,
Sep 3, 2015, 11:36:47 PM9/3/15
to AngularJS
Wow, this is even better than using the loadCSS!
I'll implementing in my app right now.

Thank you very much!

Sander Elias

unread,
Sep 3, 2015, 11:49:22 PM9/3/15
to AngularJS
Hi Donald,

You are welcome!

Regards
Sander

Craig McMurray

unread,
Sep 4, 2015, 7:13:20 AM9/4/15
to AngularJS
Sander,

I did look at the blog post and while that is a cool solution for lazy-loading/async CSS, it won't work in my case for performance reasons. My compiled CSS bundle is ~275k and my browserified JS bundle is ~375k (including all dependencies such as angular/angular modules, lo-dash, localforage, etc.).  I initiate the CSS download immediately and then the JS download in PARALLEL...I don't/can't wait for Angular to initiate to start downloading my CSS because that is wasting precious (milli) seconds especially on mobile.

Sander Elias

unread,
Sep 4, 2015, 8:45:01 AM9/4/15
to AngularJS
Hi Craig,

Well, I don't like to make my bundles that large. I just bundle my own code, and load my dependencies from CDN's. that way the JS is loaded in separate concurrent parts, and it won't take as long to load and process as 1 big bundle. Usually I end up with a pagespeed score of 93+ wich is good enough for me. Even for mobile ;)

Regards
Sander

Donald Silveira

unread,
Sep 4, 2015, 11:22:25 AM9/4/15
to AngularJS
Hi again Sander,

You think that is better loading the dependencies (Bootstrap, Font-Awesome, Angular, etc) from CDN's and just build and load the SASS/JS files of my app?
I talk about that, because I started my APP using a structure with CND's, but in the longger time I was removing CDN's and building in 1 bundle.

And now I'm confused on what to decide. This is my first "big application" and I don't want start it wrong. hahaha

Sander Elias

unread,
Sep 5, 2015, 12:39:15 AM9/5/15
to AngularJS
Hi Donald,

Yes, I do think that's better. This are some of the reason's in no particular order:
  • spread the traffic over different origins. this leads to faster boot-time
  • less traffic to my own server, so it can serve more users, or faster (oh and cheaper too ;) )
  • depending on CDN, resources are closer to the user, again faster loading
  • much greater change of a cache-hit by the user. Again faster loading
  • maintained by a company that has made a business out of this. So keeping it up is of vital importance to them. (some free cdn's excluded here)
This is the only downside:
  • Out of my control. Might go down while I'm not looking
About that last point, see the last one of the pluses. If the stuff that's hosted on there is really needed for my critical path, I can host a backup-set of resources on my own server. Heck I can even keep a list of different CDN's that have that resource and cycle trough that. If none of them are up, I can always server the resource from my server. Of course my own servers/applications never-ever-never go down ;) In case of failure do not forget to inform your user. Tell them there is network congestion, and it will take a bit longer. If all fails, don't leave them hanging.

Oh, it just hit me. If you are using free CDN's (also those are not all alike, the free Google CDN is probably a lot more reliable then some unnamed never-heard of one) there might be some slowdowns, or even unavailability. This can usually be cured by selecting a better one, and having the backup-strategy in place.  Or just use a paid one. Changes are, your app will seldom go over the free tier those provide;)

Regards
Sander 

Jan Bobisud

unread,
Dec 7, 2015, 9:40:29 AM12/7/15
to AngularJS
Hi All,

I've found this discussion because score of our app was decreased few weeks ago. We used solution proposed by Google itself to "Eliminate render-blocking JavaScript and CSS in above-the-fold content" using requestAnimationFrame method. So I've tried solution proposed by Sander with 'ng-href', but this solution does not work anymore (?).


Does anyone encountered this too?

Regards,
Jan
Reply all
Reply to author
Forward
0 new messages