Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building Progressive Web Apps with Polymer

Rob Dodson
November 20, 2015

Building Progressive Web Apps with Polymer

Watch the video: https://www.youtube.com/watch?v=g7f1Az5fxgU&index=10&list=PLNYkxOF6rcICcHeQY02XLvoGL34rZFWZn

With the combination of Web Components and Service worker, web developers have incredibly powerful and fundamentally new tools to build applications. We’ll explain how we on the Polymer team see these two technologies creating a whole new paradigm for building applications on the web, and how Polymer can help make it easier to build this brand-new breed of web application.

Rob Dodson

November 20, 2015
Tweet

More Decks by Rob Dodson

Other Decks in Technology

Transcript

  1. ?

  2. Progressive Web Apps are… Progressive Apps: Escaping Tabs Without Losing

    Our Soul — “just websites that took all the right vitamins”
  3. We want to provide elements that can support any app's

    layout, not just for Material Design
  4. <!-- top toolbar --> <app-toolbar> <!-- menu button --> <paper-icon-button

    icon=“menu"> </paper-icon-button> </app-toolbar>
  5. <!-- top toolbar --> <app-toolbar> <!-- menu button --> <paper-icon-button

    icon=“menu"> </paper-icon-button> </app-toolbar> <!-- bottom toolbar --> <app-toolbar> <div>ZUPERK&Uuml;LBLOG</div> </app-toolbar>
  6. <app-header> <!-- top toolbar --> <app-toolbar> <!-- menu button -->

    <paper-icon-button icon=“menu"> </paper-icon-button> </app-toolbar> <!-- bottom toolbar --> <app-toolbar> <div>ZUPERK&Uuml;LBLOG</div> </app-toolbar> </app-header>
  7. <app-header-layout> <app-header fixed waterfall> <!-- top toolbar --> <app-toolbar>…</app-toolbar> <!--

    bottom toolbar --> <app-toolbar>…</app-toolbar> </app-header> <main> <!-- Site content --> </main> </app-header-layout> add attributes for effects
  8. <!doctype html> <html lang="en"> <head> <script src=“webcomponents-lite.min.js” async></script> <link rel="import"

    href=“elements/elements.html" async> </head> <body unresolved> <blog-app></blog-app> </body> </html> async unblocks the renderer
  9. <!doctype html> <html lang="en"> <head> <script src=“webcomponents-lite.min.js” async></script> <link rel="import"

    href=“elements/elements.html" async> </head> <body unresolved> <!-- site content --> </body> </html>
  10. <!doctype html> <html lang="en"> <head> <link rel="import" href=“elements/elements.html" async> </head>

    <body unresolved> <!-- site content —> <script src="app.js" async></script> </body> </html> feature detection
  11. var webComponentsSupported = ('registerElement' in document && 'import' in document.createElement('link')

    && 'content' in document.createElement('template')); do we need polyfills?
  12. 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 = 'webcomponents-lite.min.js'; script.onload = finishLazyLoading; document.head.appendChild(script); } else { finishLazyLoading(); } lazy load ‘em
  13. <!doctype html> <html lang="en"> <head> <link rel="import" href=“elements/elements.html" async> </head>

    <body unresolved> <!-- site content —> <script src="app.js" async></script> </body> </html>
  14. <!doctype html> <html lang="en"> <head> <link rel="import" href=“elements/elements.html" async> </head>

    <body unresolved> <!-- site content —> <script src="app.js" async></script> </body> </html>
  15. <!doctype html> <html lang="en"> <head> <link rel="import" href=“elements/elements.html" async> </head>

    <body unresolved> <!-- site content —> <script src="app.js" async></script> </body> </html> unresolved is a bottleneck
  16. <!doctype html> <html lang="en"> <head> <link rel="import" href=“elements/elements.html" async> </head>

    <body> <!-- site content —> <script src="app.js" async></script> </body> </html>
  17. <!doctype html> <html lang=“en"> <head> <link rel="import" href=“elements/elements.html" async> <style>

    app-header-layout:unresolved { height: 192px; background: #FFF; } </style> </head> <body> <app-header-layout> … </app-header-layout> <script src="app.js" async></script> </body> </html> style unupgraded elements
  18. <!doctype html> <html lang=“en"> <head> <link rel="import" href=“elements/elements.html" async> <style>

    app-header-layout[unresolved] { height: 192px; background: #FFF; } </style> </head> <body> <app-header-layout unresolved> … </app-header-layout> <script src="app.js" async></script> </body> </html> manage attributes
  19. <html lang="en"> <head> <link rel="import" href=“elements/elements.html" async> <style> #skeleton .header

    { height: 192px; background: #FFF; } </style> </head> <body> <div id=“skeleton”> <div class=“header”>…</div> </div> <blog-app></blog-app> <script src="app.js" async></script> </body> </html> fast paint skeleton while element upgrades
  20. A service worker is a script that is run by

    your browser in the background, separate from a web page” Introduction to Service Worker — “
  21. // updated service worker is activated. var CACHE_VERSION = 1;

    var CURRENT_CACHES = { 'read-through': 'read-through-cache-v' + CACHE_VERSION }; self.addEventListener('activate', function(event) { // Delete all caches that aren't named in CURRENT_CACHES. // While there is only one cache in this example, the same logic will handle the case where // there are multiple versioned caches. var expectedCacheNames = Object.keys(CURRENT_CACHES).map(function(key) { return CURRENT_CACHES[key]; }); event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (expectedCacheNames.indexOf(cacheName) == -1) { // If this cache name isn't present in the array of "expected" cache names, then delete it. console.log('Deleting out of date cache:', cacheName); return caches.delete(cacheName);
  22. Elements to turn your web page into a true web

    app, with push, offline, bluetooth and more.
  23. Elements to turn your web page into a true web

    app, with push, offline, bluetooth and more. progressive
  24. Caching Strategies fastest: race the cache and network networkFirst: try

    the network, then the cache networkOnly: try the network, else fail
  25. { "cacheId": "zuperkulblog", "disabled": false, "precache": [ "data\/art.json", "data\/film.json", "scripts\/app.js",

    ".\/", "bower_components\/webcomponentsjs\/webcomponents-lite.min.js" ], "precacheFingerprint": "847c082ce8e3eb8c54054a7cdc76544e" } Example cache-config.json
  26. { "cacheId": "zuperkulblog", "disabled": false, "precache": [ "data\/art.json", "data\/film.json", "scripts\/app.js",

    ".\/", "bower_components\/webcomponentsjs\/webcomponents-lite.min.js" ], "precacheFingerprint": "847c082ce8e3eb8c54054a7cdc76544e" } Example cache-config.json Change this to tell SW to precache files again
  27. Check out the cache-config gulp task in the Polymer Starter

    Kit project for an example of generating at build time Polymer Starter Kit, gulpfile.js —
  28. Your App Must… ✓ Have a Web App Manifest ✓

    Have a registered Service Worker ✓ Be served over HTTPS ✓ Be visited twice, with at least 5 minutes between visits* Increasing engagement with Web App install banners —
  29. Your App Must… ✓ Have a Web App Manifest ✓

    Have a registered Service Worker ✓ Be served over HTTPS ✓ Be visited twice, with at least 5 minutes between visits* Increasing engagement with Web App install banners — may change
  30. { "name": "Zuperkülblog", "short_name": "Zuperkülblog", "icons": [{ "src": "images/touch/icon-128x128.png", "sizes":

    "128x128", "type": "image/png" }, … { "src": "images/touch/chrome-splashscreen-icon-384x384.png", "sizes": "384x384", "type": "image/png" }], "start_url": "/?homescreen=1", "background_color": "#3E4EB8", "display": "standalone", "theme_color": "#FFFFFF" }
  31. { "name": "Zuperkülblog", "short_name": "Zuperkülblog", "icons": [{ "src": "images/touch/icon-128x128.png", "sizes":

    "128x128", "type": "image/png" }, … { "src": "images/touch/chrome-splashscreen-icon-384x384.png", "sizes": "384x384", "type": "image/png" }], "start_url": "/?homescreen=1", "background_color": "#3E4EB8", "display": "standalone", "theme_color": "#FFFFFF" }
  32. <!-- In your index.html —> <!-- Web Application Manifest -->

    <link rel="manifest" href="manifest.json">
  33. <!-- Tile color for Win8 --> <meta name="msapplication-TileColor" content="#3372DF"> <!--

    Add to homescreen for Chrome on Android --> <meta name="mobile-web-app-capable" content="yes"> <meta name="application-name" content="PSK"> <link rel="icon" sizes="192x192" href="images/touch/chrome-touch-icon-192x192.png"> <!-- Add to homescreen for Safari on iOS --> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-title" content="Polymer Starter Kit"> <link rel="apple-touch-icon" href="images/touch/apple-touch-icon.png"> <!-- Tile icon for Win8 (144x144) --> <meta name="msapplication-TileImage" content="images/touch/ms-touch-icon-144x144-precomposed.png">
  34. <!-- Tile color for Win8 --> <meta name="msapplication-TileColor" content="#3372DF"> <!--

    Add to homescreen for Chrome on Android --> <meta name="mobile-web-app-capable" content="yes"> <meta name="application-name" content="PSK"> <link rel="icon" sizes="192x192" href="images/touch/chrome-touch-icon-192x192.png"> <!-- Add to homescreen for Safari on iOS --> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-title" content="Polymer Starter Kit"> <link rel="apple-touch-icon" href="images/touch/apple-touch-icon.png"> <!-- Tile icon for Win8 (144x144) --> <meta name="msapplication-TileImage" content="images/touch/ms-touch-icon-144x144-precomposed.png"> HMmmm…
  35. Polymer Starter Kit gives you a working manifest, meta tags,

    and device icons. Polymer Starter Kit —
  36. { "name": "Zuperkülblog", "short_name": "Zuperkülblog", "icons": [{ "src": "images/touch/icon-128x128.png", "sizes":

    "128x128", "type": "image/png" }, … { "src": "images/touch/chrome-splashscreen-icon-384x384.png", "sizes": "384x384", "type": "image/png" }], "start_url": "/?homescreen=1", "background_color": "#3E4EB8", "display": "standalone", "theme_color": "#FFFFFF" } oh hai! :)
  37. By default, the request URL must exactly match the URL

    used to store the cached response, including any query parameters in the search portion of the URL.” Service Workers in Production — “
  38. // On your server… app.get('/notification-data.json', function (req, res) { res.json({

    'title': 'Zuperkülblog just posted…', 'message': 'Demystifying Density by Sebastien Gabriel', 'url': ‘https://zuperkulblog.appspot.com/…’, 'icon': '/images/article/demystifying-192x192.png', 'tag': 'zuperkulblog-push-notification' }); });
  39. The food in my bowl Is old, and more to

    the point Contains no tuna.
  40. you

  41. credits Images by Paul F., Jinhwan Kim, Wolff, Pirog Tetyana,

    Dani Rolli, Julien Deveaux, Matt Brooks, Pablo Rozenberg, Ribbla Team, Jennifer Goodman, Dan Lowenstein, Blaise Sewell, Arturo Alejandro Romo Escartin Project thanks! @rob_dodson +RobDodson github.com/polymerlabs/zuperkulblog-progressive source