Building Progressive Web Apps with Polymer

95c3a3b33ea51545229c625bef42e343?s=47 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.

95c3a3b33ea51545229c625bef42e343?s=128

Rob Dodson

November 20, 2015
Tweet

Transcript

  1. None
  2. Building Progressive Web Apps with Polymer Rob Dodson @rob_dodson +RobDodson

  3. So I was on vacation.

  4. So I was on vacation. In the Netherlands.

  5. Progressive Web Apps!

  6. ?

  7. Progressive Web Apps are… Responsive Connectivity independent App-like-interactions Etc… Progressive

    Apps: Escaping Tabs Without Losing Our Soul —
  8. Progressive Web Apps are… Progressive Apps: Escaping Tabs Without Losing

    Our Soul — “just websites that took all the right vitamins”
  9. A Progressive Web App built with Polymer

  10. Responsive Load Fast Work Offline Installable Engaging our focus for

    today
  11. Make It Responsive With app-layout Elements

  12. We want to provide elements that can support any app's

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

    icon=“menu"> </paper-icon-button> </app-toolbar>
  14. <!-- 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>
  15. <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>
  16. <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
  17. paper-header-panel app-header-layout

  18. • polymerlabs.github.io/app-layout

  19. Responsive Load Fast Work Offline Installable Engaging Responsive

  20. Make It Load Fast With async patterns

  21. None
  22. <!doctype html> <html lang="en"> <head> <script src=“webcomponents-lite.min.js"></script> <link rel="import" href="elements/elements.html">

    </head> <body unresolved> <!-- site content --> </body> </html>
  23. <!doctype html> <html lang="en"> <head> <script src=“webcomponents-lite.min.js"></script> <link rel="import" href="elements/elements.html">

    </head> <body unresolved> <!-- site content --> </body> </html> Render blocking
  24. Make sure you’re not blocking the renderer waiting on polyfills

    or elements
  25. <!doctype html> <html lang="en"> <head> <script src=“webcomponents-lite.min.js"></script> <link rel="import" href="elements/elements.html">

    </head> <body unresolved> <!-- site content --> </body> </html>
  26. <!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
  27. Should you load the polyfills in every browser?

  28. Should you load the polyfills in every browser? No.

  29. <!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>
  30. <!doctype html> <html lang="en"> <head> <link rel="import" href=“elements/elements.html" async> </head>

    <body unresolved> <!-- site content --> </body> </html>
  31. <!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
  32. var webComponentsSupported = ('registerElement' in document && 'import' in document.createElement('link')

    && 'content' in document.createElement('template')); do we need polyfills?
  33. 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
  34. <!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>
  35. <!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>
  36. <!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
  37. <!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>
  38. Use the :unresolved pseudo-class to style unupgraded elements browser must

    support Custom Elements * *
  39. <!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
  40. <!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
  41. Create a skeleton that mimics the look of your app

    shell.
  42. <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
  43. original version async + skeleton

  44. Responsive Load Fast Work Offline Installable Engaging Responsive Load Fast

  45. Make It Work Offline With platinum-sw Elements

  46. A service worker is a script that is run by

    your browser in the background, separate from a web page” Introduction to Service Worker — “
  47. // 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);
  48. Libraries like sw-toolbox and sw-precache abstract these low level complexities

  49. None
  50. Elements to turn your web page into a true web

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

    app, with push, offline, bluetooth and more. progressive
  52. <!-- In your index.html —> <platinum-sw-register></platinum-sw-register>

  53. <platinum-sw-register auto-register skip-waiting clients-claim> </platinum-sw-register>

  54. <platinum-sw-register auto-register skip-waiting clients-claim> <platinum-sw-cache></platinum-sw-cache> </platinum-sw-register> Take advantage of sw-toolbox

  55. Caching Strategies fastest: race the cache and network networkFirst: try

    the network, then the cache networkOnly: try the network, else fail
  56. <platinum-sw-register auto-register skip-waiting clients-claim> <platinum-sw-cache default-cache-strategy="fastest"> </platinum-sw-cache> </platinum-sw-register> Configure your

    caching strategy
  57. Prefetch everything for your app shell

  58. <platinum-sw-register auto-register skip-waiting clients-claim> <platinum-sw-cache default-cache-strategy="fastest" prefetch=‘[“scripts/app.js”, …]’> </platinum-sw-cache> </platinum-sw-register>

    Prefetch an array of file paths
  59. <platinum-sw-register auto-register skip-waiting clients-claim> <platinum-sw-cache default-cache-strategy="fastest" cache-config-file=“/cache-config.json"> </platinum-sw-cache> </platinum-sw-register> …Or

    a json file
  60. { "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
  61. { "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
  62. 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 —
  63. None
  64. Responsive Load Fast Work Offline Installable Engaging Responsive Load Fast

    Work Offline
  65. Make It Installable With a Web App install banner

  66. 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 —
  67. 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
  68. { "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" }
  69. { "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" }
  70. <!-- In your index.html —> <!-- Web Application Manifest -->

    <link rel="manifest" href="manifest.json">
  71. Be sure to also include fallback meta tags.

  72. <!-- 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">
  73. <!-- 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…
  74. “But, I’m all lazy… ’n stuff…”

  75. Polymer Starter Kit gives you a working manifest, meta tags,

    and device icons. Polymer Starter Kit —
  76. So… Is This Thing Working? Debugging your banner

  77. chrome://flags/#bypass-app-banner-engagement-checks Enable the bypass flag to test your banner

  78. Watch Out For This Gotcha Revenge of the offline dino

  79. { "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! :)
  80. 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 — “
  81. None
  82. Responsive Load Fast Work Offline Installable Engaging Responsive Load Fast

    Work Offline Installable
  83. Make It Engaging With Push Notifications

  84. Don’t spam.

  85. Don’t spam. Seriously.

  86. Let the user decide when they want to opt-in to

    push messaging.
  87. None
  88. <paper-toggle-button></paper-toggle-button> <platinum-push-messaging></platinum-push-messaging>

  89. <paper-toggle-button checked=“{{pushEnabled}}”> </paper-toggle-button> <platinum-push-messaging enabled=“{{pushEnabled}}”> </platinum-push-messaging>

  90. <paper-toggle-button checked=“{{pushEnabled}}”> </paper-toggle-button> <platinum-push-messaging enabled=“{{pushEnabled}}” message-url="/notification-data.json"> </platinum-push-messaging>

  91. // 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' }); });
  92. None
  93. github.com/notwaldorf/caturday-post

  94. The food in my bowl Is old, and more to

    the point Contains no tuna.
  95. Responsive Load Fast Work Offline Installable Engaging Responsive Load Fast

    Work Offline Installable Engaging
  96. In Closing… (yes it’s finally over)

  97. you

  98. you the web

  99. Things are getting kind of awesome

  100. None
  101. itshackademic.com

  102. 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