ccahillmn
6/27/2015 - 12:20 PM

Fast Polymer app loading - optimized for first render, progressively enhanced lazy loading

Fast Polymer app loading - optimized for first render, progressively enhanced lazy loading

<!DOCTYPE html>
<html>
<head>
  <style>
    body.loading #splash {
      opacity: 1;
    }
    #splash {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      transition: opacity 300ms cubic-bezier(0,0,0.2,1);
      opacity: 0;
      will-change: opacity;
      z-index: 1;
      background: url(...) no-repeat;
      background-color: #E53935;
    }
  </style>

  <!-- 1. Async HTML Imports do not block rending. Benefit of keeping it declarative
       (instead of dynamically loading it later in JS) is that the parser can go
       to town pre-fetching resources, etc. -->
  <link rel="import" id="bundle" href="elements.html" async>
</head>
<!-- 2. Don't use <body unresolved>. It's a simple FOUC solution, but hides
     the page until imports and Polymer are loaded. Intead, control FOUC manually with
     a splash screen. -->
<body class="loading">
  <!-- 3. Light weight splash screen is outside of Polymer/imports and styled by
       the main page. 1st paint is fast, even on polyfilled browsers. Alternatively,
       one could create an "app shell" and style the page's un-upgraded elements
       similar to their final upgraded version. -->
  <div id="splash"></div>

  <!-- Elements wait on the page and are upgraded when elements.html loads. -->
  <paper-drawer-panel>
    ...
  </paper-drawer-panel>

  <script src="app.js" async></script>
</body>
</html>
// 4. Conditionally load the webcomponents polyfill if needed by the browser.
// This feature detect will need to change over time as browsers implement
// different features.
var webComponentsSupported = ('registerElement' in document
    && 'import' in document.createElement('link')
    && 'content' in document.createElement('template'));

if (!webComponentsSupported) {
  var script = document.createElement('script');
  script.async = true;
  script.src = '/bower_components/webcomponentsjs/webcomponents-lite.min.js';
  script.onload = finishLazyLoading;
  document.head.appendChild(script);
} else {
  finishLazyLoading();
}

function finishLazyLoading() {
  // (Optional) Use native Shadow DOM if it's available in the browser.
  window.Polymer = window.Polymer || {dom: 'shadow'};

  // 6. Fade splash screen, then remove.
  var onImportLoaded = function() {
    var loadEl = document.getElementById('splash');
    loadEl.addEventListener('transitionend', loadEl.remove);

    document.body.classList.remove('loading');

    // App is visible and ready to load some data!
  };

  var link = document.querySelector('#bundle');

  // 5. Go if the async Import loaded quickly. Otherwise wait for it.
  // crbug.com/504944 - readyState never goes to complete until Chrome 46.
  // crbug.com/505279 - Resource Timing API is not available until Chrome 46.
  if (link.import && link.import.readyState === 'complete') {
    onImportLoaded();
  } else {
    link.addEventListener('load', onImportLoaded);
  }
}