OPTIMIZING

CRITICAL RENDERING PATH

What is
CRITICAL RENDERING PATH?

Delay User Reaction
0 - 100ms Instant
100 - 300ms Small perceptible delay
300 - 1000ms Machine is working
1s+ Mental context switch
10s+ I'll come back later...

Source :- @igrigorik

RENDERING PATH

BROWSER

NETWORK

DOM

CSSOM

HTML

CSS

JS

RENDER TREE   +

LAYOUT

+

PAINT

BROWSER

NETWORK

How to optimise

DNS  LOOKUP

TCP

SSL 

REQUEST

 1. dns - prefetch

<link rel='dns-prefetch' href='http://example.com'>

 2. preconnect

<link rel='preconnect' href='http://example.com'>

There are couples of things also like prerender, prefetch & preload

How much would this all save us?

Don't guess it, test it

                                                          - @aerotwist

PRECONNECT

DOMAIN SHARDING

But, there is one more to it

What about rest of  Rendering Path ?

START  STREAMING  /HTML

START  BUILDING  DOM

GET /CSS

START  BUILDING  CSSOM

JAVASCRIPT

CSSOM COMPLETE

EXECUTE   JAVASCRIPT

CONTINUE BUILDING DOM

RENDER PAGE

THINGS   SLOWING   US   DOWN

  • CSS - Render Blocking
  • Synchronous JAVASCRIPT - Parser Blocking

When you want to be fast, you have to give up things slowing you down.

- @addyosmani

Can we ?

PARTIALLY YES

Resolving CSS issue

  • Declaring all CSS at top

  • Inline CSS for whole page

  • Inline only CRITICAL CSS   

Progressively enhanced rest of it.

Tools  to  find  CRITICAL  CSS

  • Critical CSS by Addy Osmani
     
  • CriticalCSS by Filament Group
     
  • Homo Sapiens  
<head>
  <script>
    function loadCSS(src) {
        'use strict';
        const ref = document.getElementsByTagName('script')[0];
        const link = document.createElement('link');
        link.rel = 'stylesheet'; // this line is important
        link.href = src;
        ref.parentNode.insertBefore(link, ref);      
    }
  </script>
  <style>
    .critical {
        //styles
    }
    .non-critical {
        display: none;
    }
  </style>
  <script>
    loadCSS('/non-critical.css);
  </script>
</head>
<body>
</body>

How to load CSS progressively ?

- Script injected <link rel = 'stylesheet'>

Wait,  isn't  this  thing  blocking?

//Based on https://jakearchibald.com/2016/link-in-body/
function isScriptInjectedStyleBlocking(browser) {
    if(browser == Chrome || broswer == Safari) {
        return true;
    } else {
        //Even if it's Chrome Canary, so it might land in Chrome too in future
        return false;
    }
}

We have a better way

'use strict';
const ref = document.getElementsByTagName('script')[0];
const link = document.createElement('link');
//below two lines are important
link.rel = 'stylesheet';
link.media = 'only whatever';
link.href = 'http://example.com/style.css';
ref.parentNode.insertBefore(link, ref);
setTimeout(_ => {
    link.media = 'all';
},0);

- Script injected <link rel = 'stylesheet'> with media attr.

Is this the best we can do ?

function isEnhancementPossible() {
    if( timeMachine === 'Present' && browser !== 'IE/Edge') {
        return false;
    } else {
        //Yes!!! Apparently IE/Edge provided the solution.
        //And it worked for firefox as a hack.
        return true;
    }

}

What's  the  enhancement?

  • Each <link rel="stylesheet"> should block rendering of subsequent content.
  • But, allow the rendering of content before it.
  • All stylesheets should load in parallel.
  • But, they should apply in series to prevent FOUC.

Idea is :-

So much of text but where's code..?

 How a page would look like in reality?

<!-- Taken from https://jakearchibald.com/2016/link-in-body/  <3 -->
<head>
</head>
<body>
  
    <link rel="stylesheet" href="/site-header.css">
    <header>..</header>

    <link rel="stylesheet" href="/article.css">
    <main>..</main>

    <link rel="stylesheet" href="/comment.css">
    <section class='comments'>..</section>

    <link rel="stylesheet" href="/about-me.css">
    <section class='about-me'>..</section>

    <link rel="stylesheet" href="/footer.css">
    <footer>..</footer>

</body>

THINGS   SLOWING   US   DOWN

  • CSS - Render Blocking
  • Synchronous JAVASCRIPT - Parser Blocking

Resolving  JS  issue

 -  Include  scripts  before end of <body>

<body>
    ..rest of markup
    <script src="//my-awesome-library.js"></script>
    <script src="analytics.js"></script>
</body>

 -  'defer'  attribute  on  <script/>

<script src="//my-awesome-library.js" defer></script>
<script src="analytics.js" defer></script>

 -  'aysnc'  attribute  on  <script/>

<script src="//my-awesome-library.js" async></script>
<script src="analytics.js" async></script>

Continued ...

 -  Script injected <script/>

'use strict';
[
    '//my-awesome-library.js',
    'analytics.js'
].forEach(link => {
    const script = document.createElement('script');
    script.src = src;
    document.head.appendChild(script);
});

 -  Script injected <script/> with async = false

'use strict';
[
    '//my-awesome-library.js',
    'analytics.js'
].forEach(link => {
    const script = document.createElement('script');
    script.src = src;    
    script.async = false;
    document.head.appendChild(script);
});

THINGS   SLOWING   US   DOWN

  • CSS - Render Blocking
  • Synchronous JAVASCRIPT - Parser Blocking

Congrats!!!!!

We can now ship our web app in 1000 ms.

Few days later........

Developer: What is this dude!! I have done all optimizations told by that conference guy and still my website is not loading in 1000ms.

I am feeling cheated.

On twitter/Insta :- #feelingCheated #conferenceGuy #whatToDo

But  the  problem  actually  is....

Custom  FONTS

P.S. :- Fonts are great tool.

Relation  between  Fonts  and  Rendering 

  1. Fonts block  text-rendering  until  loaded.
     
  2. Fonts are loaded lazily by browser.
                             OR
    Font's requests are dispatched after render tree is created.

Solutions :-

  1. Use font loading tools like 'fontfaceobserver', 'loadFonts' .
     
  2. Provide a fallback font while actual font is loading.
<head>
    <link rel="stylesheet" href="main.css">
    <link rel="stylesheet" href="font.css" media="none" 
onload="if(media!='all')media='all'">
    <style>
    body {
      font-family: MyFancyFont, "Times New Roman", ...;
    }
    </style>
</head>
<body>
</body>
/* Content of fonts.css*/
@font-face {
  font-family: MyFancyFont;
  font-style: normal;
  font-weight: 400;
  src: local('MyFancyFont'), 
        url('data:application/x-font-woff;charset=utf-8;base64,...')
}

3. Use font-loading API (https://drafts.csswg.org/css-font-loading/)

//Source :- 
//https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/webfont-optimization

var font = new FontFace("myAwesomeFont", "url(/fonts/MyAwesomeFonts.woff2)", {
  style: 'normal', weight: '400'
});

font.load(); // don't wait for render tree, initiate immediate fetch!

font.ready().then(function() {
  // apply the font (which may re-render text and cause a page re-flow)
  // once the font has finished downloading
  document.fonts.add(font);
  document.body.style.fontFamily = "Awesome Font, serif";

  // OR... by default content is hidden, and rendered once font is available
  var content = document.getElementById("content");
  content.style.visibility = "visible";

  // OR... apply own render strategy here... 
});

Support :- http://caniuse.com/#feat=font-loading

Now, our beloved developer is happy as he/she can render first paint of web app in under 1000ms

We all are now feeling ..

DEPENDS

Can't  we  really  load  all  non-critical  resources  through  a unique method or API ?

#depressed

#heart</3>

#lifeOfWebDev :3

Wait, depends on ....?

function bestResourceLoadingMethod() {
    return new Promise(resolve,reject) {
            if(isConditionalLoading()) {
                return resolve();
            } else {
                return reject();
            }
    }
}

bestResourceLoadingMethod()
    .then(_ => console.log('Yes :3'))
    .catch(_ => console.log('Future beholds something for you :D'));

w3c has new spec for loading resources in parallel and it's 

Preload

Cautions :-

- Spec is still in draft mode.

- Currently, supported in Chrome only

Examples of using Preload 

<head>
    <!-- preload this resource as stylesheet -->
    <link rel="preload" href="/script/my-awesome-library.js" as="script">
</head>
<body>
    
    <script>
        if( needMyAwesomeLibrary ) {
            const lib = document.createElement('script);
            //Now, browser should automatically get from cache if preloaded
            lib.src = "/script/my-awesome-library.js"; 
            document.head.appendChild(lib);
        }
    </script>
</body>

Supported value of 'as' are :-

 media         

 script         

 style

 font

 image

 object

 document

 worker

 embed

Pro Tips 

#1 Reduce Number of HTTP Requests.

#3 Gzip everything.

#2 Don't abuse #1

#4 Never use CSS @imports

#5 Keep JavaScript that will progressively load non-critical things at top before <link rel = "stylesheet">

#6 window.onload is not a right metric to stop showing loading spinner.

#7 Try to cache fonts if you can.

#8 Use rAf for animation.

#9 Use requestIdleCallback() for non-essential stuff.

#10 Use Streams if you can (https://jakearchibald.com/2016/streams-ftw/)

#11 Use Service Worker API to enhance repetitive rendering :D .

THANKS!

/aboutme

/name

TARUN GARG

/age

Approaching 21

/github

@tarungarg546

/twitter

@Tarun_Garg2

/facebook

@tarungarg546

/experience

Fresher

Thanks Again!

Any Questions?

Recommended People to follow for more :-

@addyosmani           @aerotwist        @paulirish      @igrogorik

 

@daviswalsh              @getifyJS            @souders      @grigs

 

@scottjehl                   @chromiumdev   @yoavweiss and lot more......