A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 118KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang=fr>
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset=utf-8>
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name=viewport content="width=device-width,minimum-scale=1,initial-scale=1,shrink-to-fit=no">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>The offline cookbook (archive) — David Larlet</title>
  13. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  14. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons/apple-touch-icon.png">
  15. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons/favicon-32x32.png">
  16. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons/favicon-16x16.png">
  17. <link rel="manifest" href="/manifest.json">
  18. <link rel="mask-icon" href="/static/david/icons/safari-pinned-tab.svg" color="#5bbad5">
  19. <link rel="shortcut icon" href="/static/david/icons/favicon.ico">
  20. <meta name="apple-mobile-web-app-title" content="David Larlet">
  21. <meta name="application-name" content="David Larlet">
  22. <meta name="msapplication-TileColor" content="#da532c">
  23. <meta name="msapplication-config" content="/static/david/icons/browserconfig.xml">
  24. <meta name="theme-color" content="#f0f0ea">
  25. <!-- That good ol' feed, subscribe :p. -->
  26. <link rel=alternate type="application/atom+xml" title=Feed href="/david/log/">
  27. <meta name="robots" content="noindex, nofollow">
  28. <meta content="origin-when-cross-origin" name="referrer">
  29. <!-- Canonical URL for SEO purposes -->
  30. <link rel="canonical" href="http://jakearchibald.com/2014/offline-cookbook/">
  31. <style>
  32. /* http://meyerweb.com/eric/tools/css/reset/ */
  33. html, body, div, span,
  34. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  35. a, abbr, address, big, cite, code,
  36. del, dfn, em, img, ins,
  37. small, strike, strong, tt, var,
  38. dl, dt, dd, ol, ul, li,
  39. fieldset, form, label, legend,
  40. table, caption, tbody, tfoot, thead, tr, th, td,
  41. article, aside, canvas, details, embed,
  42. figure, figcaption, footer, header, hgroup,
  43. menu, nav, output, ruby, section, summary,
  44. time, mark, audio, video {
  45. margin: 0;
  46. padding: 0;
  47. border: 0;
  48. font-size: 100%;
  49. font: inherit;
  50. vertical-align: baseline;
  51. }
  52. /* HTML5 display-role reset for older browsers */
  53. article, aside, details, figcaption, figure,
  54. footer, header, hgroup, menu, nav, section { display: block; }
  55. body { line-height: 1; }
  56. blockquote, q { quotes: none; }
  57. blockquote:before, blockquote:after,
  58. q:before, q:after {
  59. content: '';
  60. content: none;
  61. }
  62. table {
  63. border-collapse: collapse;
  64. border-spacing: 0;
  65. }
  66. /* http://practicaltypography.com/equity.html */
  67. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  68. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  69. @font-face {
  70. font-family: 'EquityTextB';
  71. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  72. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  73. font-weight: 300;
  74. font-style: normal;
  75. font-display: swap;
  76. }
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: italic;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  89. font-weight: 700;
  90. font-style: normal;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'ConcourseT3';
  95. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  96. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  97. font-weight: 300;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  102. body {
  103. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  104. font-family: "EquityTextB", Palatino, serif;
  105. background-color: #f0f0ea;
  106. color: #07486c;
  107. font-kerning: normal;
  108. -moz-osx-font-smoothing: grayscale;
  109. -webkit-font-smoothing: subpixel-antialiased;
  110. text-rendering: optimizeLegibility;
  111. font-variant-ligatures: common-ligatures contextual;
  112. font-feature-settings: "kern", "liga", "clig", "calt";
  113. }
  114. pre, code, kbd, samp, var, tt {
  115. font-family: 'TriplicateT4c', monospace;
  116. }
  117. em {
  118. font-style: italic;
  119. color: #323a45;
  120. }
  121. strong {
  122. font-weight: bold;
  123. color: black;
  124. }
  125. nav {
  126. background-color: #323a45;
  127. color: #f0f0ea;
  128. display: flex;
  129. justify-content: space-around;
  130. padding: 1rem .5rem;
  131. }
  132. nav:last-child {
  133. border-bottom: 1vh solid #2d7474;
  134. }
  135. nav a {
  136. color: #f0f0ea;
  137. }
  138. nav abbr {
  139. border-bottom: 1px dotted white;
  140. }
  141. h1 {
  142. border-top: 1vh solid #2d7474;
  143. border-bottom: .2vh dotted #2d7474;
  144. background-color: #e3e1e1;
  145. color: #323a45;
  146. text-align: center;
  147. padding: 5rem 0 4rem 0;
  148. width: 100%;
  149. font-family: 'ConcourseT3';
  150. display: flex;
  151. flex-direction: column;
  152. }
  153. h1.single {
  154. padding-bottom: 10rem;
  155. }
  156. h1 span {
  157. position: absolute;
  158. top: 1vh;
  159. left: 20%;
  160. line-height: 0;
  161. }
  162. h1 span a {
  163. line-height: 1.7;
  164. padding: 1rem 1.2rem .6rem 1.2rem;
  165. border-radius: 0 0 6% 6%;
  166. background: #2d7474;
  167. font-size: 1.3rem;
  168. color: white;
  169. text-decoration: none;
  170. }
  171. h2 {
  172. margin: 4rem 0 1rem;
  173. border-top: .2vh solid #2d7474;
  174. padding-top: 1vh;
  175. }
  176. h3 {
  177. text-align: center;
  178. margin: 3rem 0 .75em;
  179. }
  180. hr {
  181. height: .4rem;
  182. width: .4rem;
  183. border-radius: .4rem;
  184. background: #07486c;
  185. margin: 2.5rem auto;
  186. }
  187. time {
  188. display: bloc;
  189. margin-left: 0 !important;
  190. }
  191. ul, ol {
  192. margin: 2rem;
  193. }
  194. ul {
  195. list-style-type: square;
  196. }
  197. a {
  198. text-decoration-skip-ink: auto;
  199. text-decoration-thickness: 0.05em;
  200. text-underline-offset: 0.09em;
  201. }
  202. article {
  203. max-width: 50rem;
  204. display: flex;
  205. flex-direction: column;
  206. margin: 2rem auto;
  207. }
  208. article.single {
  209. border-top: .2vh dotted #2d7474;
  210. margin: -6rem auto 1rem auto;
  211. background: #f0f0ea;
  212. padding: 2rem;
  213. }
  214. article p:last-child {
  215. margin-bottom: 1rem;
  216. }
  217. p {
  218. padding: 0 .5rem;
  219. margin-left: 3rem;
  220. }
  221. p + p,
  222. figure + p {
  223. margin-top: 2rem;
  224. }
  225. blockquote {
  226. background-color: #e3e1e1;
  227. border-left: .5vw solid #2d7474;
  228. display: flex;
  229. flex-direction: column;
  230. align-items: center;
  231. padding: 1rem;
  232. margin: 1.5rem;
  233. }
  234. blockquote cite {
  235. font-style: italic;
  236. }
  237. blockquote p {
  238. margin-left: 0;
  239. }
  240. figure {
  241. border-top: .2vh solid #2d7474;
  242. background-color: #e3e1e1;
  243. text-align: center;
  244. padding: 1.5rem 0;
  245. margin: 1rem 0 0;
  246. font-size: 1.5rem;
  247. width: 100%;
  248. }
  249. figure img {
  250. max-width: 250px;
  251. max-height: 250px;
  252. border: .5vw solid #323a45;
  253. padding: 1px;
  254. }
  255. figcaption {
  256. padding: 1rem;
  257. line-height: 1.4;
  258. }
  259. aside {
  260. display: flex;
  261. flex-direction: column;
  262. background-color: #e3e1e1;
  263. padding: 1rem 0;
  264. border-bottom: .2vh solid #07486c;
  265. }
  266. aside p {
  267. max-width: 50rem;
  268. margin: 0 auto;
  269. }
  270. /* https://fvsch.com/code/css-locks/ */
  271. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  272. font-size: 1rem;
  273. line-height: calc( 1.5em + 0.2 * 1rem );
  274. }
  275. h1 {
  276. font-size: 1.9rem;
  277. line-height: calc( 1.2em + 0.2 * 1rem );
  278. }
  279. h2 {
  280. font-size: 1.6rem;
  281. line-height: calc( 1.3em + 0.2 * 1rem );
  282. }
  283. h3 {
  284. font-size: 1.35rem;
  285. line-height: calc( 1.4em + 0.2 * 1rem );
  286. }
  287. @media (min-width: 20em) {
  288. /* The (100vw - 20rem) / (50 - 20) part
  289. resolves to 0-1rem, depending on the
  290. viewport width (between 20em and 50em). */
  291. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  292. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  293. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  294. margin-left: 0;
  295. }
  296. h1 {
  297. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  298. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  299. }
  300. h2 {
  301. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  302. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  303. }
  304. h3 {
  305. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. }
  309. @media (min-width: 50em) {
  310. /* The right part of the addition *must* be a
  311. rem value. In this example we *could* change
  312. the whole declaration to font-size:2.5rem,
  313. but if our baseline value was not expressed
  314. in rem we would have to use calc. */
  315. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  316. font-size: calc( 1rem + .6 * 1rem );
  317. line-height: 1.5em;
  318. }
  319. p, li, pre, details {
  320. margin-left: 3rem;
  321. }
  322. h1 {
  323. font-size: calc( 1.9rem + 1.5 * 1rem );
  324. line-height: 1.2em;
  325. }
  326. h2 {
  327. font-size: calc( 1.5rem + 1.5 * 1rem );
  328. line-height: 1.3em;
  329. }
  330. h3 {
  331. font-size: calc( 1.35rem + 1.5 * 1rem );
  332. line-height: 1.4em;
  333. }
  334. figure img {
  335. max-width: 500px;
  336. max-height: 500px;
  337. }
  338. }
  339. figure.unsquared {
  340. margin-bottom: 1.5rem;
  341. }
  342. figure.unsquared img {
  343. height: inherit;
  344. }
  345. @media print {
  346. body { font-size: 100%; }
  347. a:after { content: " (" attr(href) ")"; }
  348. a, a:link, a:visited, a:after {
  349. text-decoration: underline;
  350. text-shadow: none !important;
  351. background-image: none !important;
  352. background: white;
  353. color: black;
  354. }
  355. abbr[title] { border-bottom: 0; }
  356. abbr[title]:after { content: " (" attr(title) ")"; }
  357. img { page-break-inside: avoid; }
  358. @page { margin: 2cm .5cm; }
  359. h1, h2, h3 { page-break-after: avoid; }
  360. p3 { orphans: 3; widows: 3; }
  361. img {
  362. max-width: 250px !important;
  363. max-height: 250px !important;
  364. }
  365. nav, aside { display: none; }
  366. }
  367. ul.with_columns {
  368. column-count: 1;
  369. }
  370. @media (min-width: 20em) {
  371. ul.with_columns {
  372. column-count: 2;
  373. }
  374. }
  375. @media (min-width: 50em) {
  376. ul.with_columns {
  377. column-count: 3;
  378. }
  379. }
  380. ul.with_two_columns {
  381. column-count: 1;
  382. }
  383. @media (min-width: 20em) {
  384. ul.with_two_columns {
  385. column-count: 1;
  386. }
  387. }
  388. @media (min-width: 50em) {
  389. ul.with_two_columns {
  390. column-count: 2;
  391. }
  392. }
  393. .gallery {
  394. display: flex;
  395. flex-wrap: wrap;
  396. justify-content: space-around;
  397. }
  398. .gallery figure img {
  399. margin-left: 1rem;
  400. margin-right: 1rem;
  401. }
  402. .gallery figure figcaption {
  403. font-family: 'ConcourseT3'
  404. }
  405. footer {
  406. font-family: 'ConcourseT3';
  407. display: flex;
  408. flex-direction: column;
  409. border-top: 3px solid white;
  410. padding: 4rem 0;
  411. background-color: #07486c;
  412. color: white;
  413. }
  414. footer > * {
  415. max-width: 50rem;
  416. margin: 0 auto;
  417. }
  418. footer a {
  419. color: #f1c40f;
  420. }
  421. footer .avatar {
  422. width: 200px;
  423. height: 200px;
  424. border-radius: 50%;
  425. float: left;
  426. -webkit-shape-outside: circle();
  427. shape-outside: circle();
  428. margin-right: 2rem;
  429. padding: 2px 5px 5px 2px;
  430. background: white;
  431. border-left: 1px solid #f1c40f;
  432. border-top: 1px solid #f1c40f;
  433. border-right: 5px solid #f1c40f;
  434. border-bottom: 5px solid #f1c40f;
  435. }
  436. </style>
  437. <h1>
  438. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  439. The offline cookbook (archive)
  440. <time>Pour la pérennité des contenus liés. Non-indexé, retrait sur simple email.</time>
  441. </h1>
  442. <section>
  443. <article>
  444. <h3><a href="http://jakearchibald.com/2014/offline-cookbook/">Source originale du contenu</a></h3>
  445. <p>When AppCache arrived on the scene it gave us a couple of patterns to make content work offline. If those were the patterns you needed, congratulations, you won the AppCache lottery (the jackpot remains unclaimed), but the rest of us were left huddled in a corner <a href="http://alistapart.com/article/application-cache-is-a-douchebag">rocking back &amp; forth</a>.</p>
  446. <p>With ServiceWorker (<a href="http://www.html5rocks.com/en/tutorials/service-worker/introduction/">intro</a>) we gave up trying to solve offline, and gave developers the moving parts to go solve it themselves. It gives you control over caching and how requests are handled. That means you get to create your own patterns. Let's take a look at a few possible patterns in isolation, but in practice you'll likely use many of them in tandem depending on URL &amp; context.</p>
  447. <p>All code examples work today in <a href="https://www.google.com/chrome/browser/beta.html">Chrome 40 beta</a> with the <a href="https://github.com/coonsta/cache-polyfill">cache polyfill</a>, unless otherwise noted. This stuff will land into the stable version January/February 2015 barring any emergencies, so it won't be long until millions of real users can benefit from this stuff.</p>
  448. <p>For a working demo of some of these patterns, see <a href="https://jakearchibald.github.io/trained-to-thrill/">Trained-to-thrill</a>, and <a href="https://www.youtube.com/watch?v=px-J9Ghvcx4">this video</a> showing the performance impact.</p>
  449. <h3 id="contents">Contents</h3>
  450. <ol><li><a href="#the-cache-machine-when-to-store-resources">The cache machine - when to store resources</a><ol><li><a href="#on-install-as-a-dependency">On install - as a dependency</a></li><li><a href="#on-install-not-as-a-dependency">On install - not as a dependency</a></li><li><a href="#on-activate">On activate</a></li><li><a href="#on-user-interaction">On user interaction</a></li><li><a href="#on-network-response">On network response</a></li><li><a href="#stale-while-revalidate">Stale-while-revalidate</a></li><li><a href="#on-push-message">On push message</a></li><li><a href="#on-background-sync">On background-sync</a></li></ol></li><li><a href="#cache-persistence">Cache persistence</a></li><li><a href="#serving-suggestions-responding-to-requests">Serving suggestions - responding to requests</a><ol><li><a href="#cache-only">Cache only</a></li><li><a href="#network-only">Network only</a></li><li><a href="#cache-falling-back-to-network">Cache, falling back to network</a></li><li><a href="#cache-network-race">Cache &amp; network race</a></li><li><a href="#network-falling-back-to-cache">Network falling back to cache</a></li><li><a href="#cache-then-network">Cache then network</a></li><li><a href="#generic-fallback">Generic fallback</a></li><li><a href="#serviceworker-side-templating">ServiceWorker-side templating</a></li></ol></li><li><a href="#putting-it-together">Putting it together</a></li></ol>
  451. <h2 id="the-cache-machine-when-to-store-resources">The cache machine - when to store resources</h2>
  452. <p>ServiceWorker lets you handle requests independently from caching, so we'll look at them separately. First up, caching, when should it be done?</p>
  453. <h3 id="on-install-as-a-dependency">On install - as a dependency</h3>
  454. <p><svg class="diagram-defs"><defs><path id="diagram-path-test" d="M372.34 122.043s116.49 28.723 82.98 130.32l6.313-2.927"/></defs></svg></p>
  455. <p><svg class="diagram-defs"><defs><path d="M125.558 122.652V76.668c0-2.123-1.72-3.844-3.843-3.844l-38.2 23.772 42.043 26.056zM39.07 72.825c-2.122 0-3.843 1.72-3.843 3.844v49.964c0 2.123 1.72 3.846 3.844 3.846h82.65c2.122 0 3.842-1.722 3.842-3.845l-86.49-53.81zm59.67-11.272c-1.97 7.728-8.96 13.45-17.303 13.45-8.342 0-15.33-5.723-17.3-13.45H43.614v10.294L80.26 94.393l36.804-21.83v-11.01H98.74zM81.437 42.78c-7.924 0-14.346 6.42-14.346 14.346 0 7.925 6.427 14.35 14.35 14.35s14.35-6.425 14.35-14.35S89.36 42.78 81.44 42.78zm1.85 22.158H80.08V53.813h-3.137v-2.09h.29c1.068 0 1.91-.202 2.527-.603.615-.4 1.003-1.046 1.167-1.935h2.36v15.753z" id="diagram-notification"/><path d="M105.74 133.947l-12.07-5.73c-1.36-.644-2.184-1.647-2.184-3.043v-.358c0-1.396.823-2.4 2.184-3.044l12.07-5.73c.427-.214.82-.357 1.324-.357 1.146-.035 2.184.967 2.184 2.256 0 1.04-.573 1.76-1.504 2.19l-10.814 4.83 10.814 4.834c.93.43 1.504 1.253 1.504 2.256 0 1.325-1.038 2.256-2.365 2.22-.392 0-.786-.143-1.144-.323zm6.803 5.373l14.002-29.257c.394-.823 1.038-1.253 1.898-1.253 1.146 0 2.112.967 2.112 2.04 0 .395-.143.79-.25 1.04l-14.002 29.255c-.394.823-1.038 1.253-1.898 1.253-1.146 0-2.112-.966-2.112-2.04 0-.395.142-.788.25-1.04zm25.067-23.277l12.067 5.73c1.36.644 2.184 1.647 2.184 3.043v.358c0 1.396-.82 2.4-2.18 3.043l-12.07 5.73c-.43.215-.82.358-1.323.358-1.146.035-2.184-.967-2.184-2.256 0-1.04.574-1.76 1.505-2.19l10.814-4.834-10.814-4.835c-.93-.43-1.504-1.252-1.504-2.255 0-1.326 1.04-2.257 2.365-2.22.392 0 .786.143 1.144.322zm31.91-27.436c0-1.866-1.513-3.377-3.38-3.377H76.08c-1.865 0-3.377 1.51-3.377 3.377v66.42c0 1.865 1.512 3.377 3.377 3.377h90.06c1.867 0 3.38-1.512 3.38-3.377v-66.42zm-74.865-.188c1.347 0 2.44 1.09 2.44 2.434s-1.092 2.44-2.44 2.44c-1.347 0-2.44-1.092-2.44-2.44 0-1.347 1.094-2.44 2.44-2.44zm-6.754 0c1.35 0 2.44 1.09 2.44 2.434s-1.09 2.44-2.44 2.44c-1.343 0-2.44-1.092-2.44-2.44 0-1.347 1.098-2.44 2.44-2.44zm-6.75 0c1.347 0 2.44 1.09 2.44 2.434s-1.092 2.44-2.44 2.44c-1.347 0-2.44-1.092-2.44-2.44 0-1.347 1.094-2.44 2.44-2.44zm83.87 65.48H77.21V97.61h87.81v56.29z" id="diagram-page"/><path d="M82.687 42.615c-2.388 0-4.733.18-7.023.53l-.2-.09c-.02.044-.034.09-.054.13-22.133 3.493-39.058 22.648-39.058 45.76 0 25.59 20.744 46.334 46.334 46.334 25.588 0 46.333-20.748 46.333-46.338 0-25.587-20.748-46.33-46.337-46.33zm-9.65 51.123c-.73-.364-1.508-.646-2.328-.818.06-5.917.52-11.804 1.384-17.586 2.722-.564 5.02-2.27 6.397-4.586 6.643 1.918 13.024 4.942 19.025 8.934-.155.473-.27.964-.35 1.465-8.557 2.602-16.684 6.85-24.132 12.59zM63.51 110.73c.83.522 1.744.92 2.72 1.174.694 5.234 1.7 10.388 3.016 15.44-4.677-1.643-8.967-4.114-12.688-7.24 2.177-3.334 4.502-6.46 6.95-9.374zm52.983-26.94c.295-.004.59-.016.885-.016 1.898 0 3.8.117 5.694.288.193 1.603.303 3.232.303 4.886 0 2.454-.23 4.854-.65 7.19-2.327-3.115-4.784-6.04-7.36-8.753.595-1.09.986-2.304 1.128-3.595zm5.457-5.498c-1.524-.102-3.05-.162-4.572-.162-.652 0-1.303.017-1.952.035-1.637-3.098-4.887-5.212-8.632-5.212-2.27 0-4.356.784-6.014 2.085-6.582-4.397-13.615-7.698-20.942-9.784-.158-2.962-1.63-5.567-3.85-7.25.987-3.267 2.103-6.473 3.358-9.595 1.103-.09 2.215-.15 3.34-.15 18.75 0 34.568 12.75 39.264 30.03zM72.895 49.458c-.797 2.164-1.534 4.363-2.213 6.59-.193-.012-.382-.03-.578-.03-4.28 0-7.907 2.757-9.224 6.59-3.272.002-6.54.26-9.782.73 5.488-6.755 13.093-11.72 21.797-13.88zm-30.897 39.49c0-6.878 1.72-13.36 4.745-19.044 4.578-1.056 9.248-1.624 13.93-1.644.787 3 2.958 5.434 5.797 6.575-.9 6.113-1.376 12.333-1.414 18.582-3.59 1.44-6.126 4.95-6.126 9.054 0 1.406.302 2.738.834 3.94-2.584 3.02-5.03 6.26-7.335 9.71-6.48-7.21-10.436-16.734-10.436-27.17zm40.69 40.688c-2.436 0-4.817-.227-7.136-.64-1.63-5.636-2.852-11.42-3.66-17.315 3.817-1.32 6.56-4.94 6.56-9.21 0-1.74-.46-3.37-1.26-4.783 6.46-4.862 13.437-8.518 20.757-10.858 1.55 3.332 4.92 5.65 8.84 5.65 1.63 0 3.16-.407 4.512-1.114 3.386 3.572 6.56 7.524 9.485 11.836-5.787 15.426-20.68 26.438-38.104 26.438z" id="diagram-network"/><path d="M66.496 72.76h41.74v41.74h-41.74V72.76zm69.566 0c1.275 0 2.318-1.04 2.318-2.32 0-1.28-1.043-2.318-2.318-2.318h-9.276v-4.638h9.276c1.275 0 2.318-1.038 2.318-2.318s-1.043-2.32-2.318-2.32h-9.276V56.53c0-1.28-1.043-2.32-2.32-2.32h-2.317v-9.274c0-1.28-1.05-2.32-2.32-2.32-1.28 0-2.32 1.04-2.32 2.32v9.275h-4.64V44.94c0-1.28-1.044-2.32-2.32-2.32-1.275 0-2.318 1.04-2.318 2.32v9.275h-4.638v-9.275c0-1.28-1.044-2.32-2.32-2.32-1.274 0-2.318 1.04-2.318 2.32v9.275H94.32v-9.277c0-1.28-1.044-2.32-2.32-2.32-1.275 0-2.318 1.04-2.318 2.32v9.275h-4.638v-9.275c0-1.28-1.04-2.32-2.32-2.32-1.28 0-2.318 1.04-2.318 2.32v9.275H75.77v-9.28c0-1.28-1.04-2.32-2.32-2.32-1.28 0-2.318 1.04-2.318 2.32v9.276h-4.638V44.93c0-1.28-1.038-2.32-2.318-2.32s-2.32 1.04-2.32 2.32v9.275H57.22V44.93c0-1.28-1.04-2.32-2.32-2.32s-2.32 1.04-2.32 2.32v9.274h-2.316c-1.28 0-2.32 1.038-2.32 2.318v2.32H38.67c-1.28 0-2.318 1.038-2.318 2.318s1.038 2.318 2.318 2.318h9.276v4.638H38.67c-1.28 0-2.318 1.04-2.318 2.32 0 1.28 1.038 2.318 2.318 2.318h9.276v4.637H38.67c-1.28 0-2.318 1.04-2.318 2.32 0 1.28 1.038 2.32 2.318 2.32h9.276v4.64H38.67c-1.28 0-2.318 1.04-2.318 2.32 0 1.28 1.038 2.317 2.318 2.317h9.276v4.638H38.67c-1.28 0-2.318 1.044-2.318 2.32 0 1.274 1.038 2.318 2.318 2.318h9.276v4.638H38.67c-1.28 0-2.318 1.045-2.318 2.32 0 1.276 1.038 2.32 2.318 2.32h9.276v4.64H38.67c-1.28 0-2.318 1.04-2.318 2.317 0 1.275 1.038 2.318 2.318 2.318h9.276v4.64H38.67c-1.28 0-2.318 1.043-2.318 2.32 0 1.273 1.038 2.317 2.318 2.317h9.276v2.32c0 1.274 1.04 2.318 2.32 2.318h2.317v9.275c0 1.275 1.04 2.32 2.32 2.32 1.28 0 2.318-1.045 2.318-2.32v-9.275h4.64v9.275c0 1.275 1.04 2.32 2.32 2.32s2.32-1.045 2.32-2.32v-9.275h4.638v9.275c0 1.275 1.04 2.32 2.32 2.32 1.28 0 2.318-1.045 2.318-2.32v-9.275h4.637v9.275c0 1.275 1.04 2.32 2.32 2.32s2.32-1.045 2.32-2.32v-9.275h4.636v9.275c0 1.275 1.042 2.32 2.32 2.32 1.273 0 2.317-1.045 2.317-2.32v-9.275h4.637v9.275c0 1.275 1.045 2.32 2.32 2.32s2.32-1.045 2.32-2.32v-9.275h4.637v9.275c0 1.275 1.044 2.32 2.32 2.32 1.275 0 2.32-1.045 2.32-2.32v-9.275h4.636v9.275c0 1.275 1.045 2.32 2.32 2.32 1.276 0 2.32-1.045 2.32-2.32v-9.275h2.318c1.276 0 2.32-1.044 2.32-2.32v-2.318h9.275c1.274 0 2.317-1.045 2.317-2.32s-1.042-2.32-2.317-2.32h-9.277v-4.64h9.276c1.273 0 2.316-1.04 2.316-2.316 0-1.276-1.042-2.32-2.317-2.32h-9.277v-4.637h9.277c1.276 0 2.32-1.043 2.32-2.32s-1.043-2.32-2.318-2.32h-9.276v-4.637h9.276c1.275 0 2.318-1.044 2.318-2.32 0-1.274-1.042-2.318-2.317-2.318h-9.277V91.31h9.276c1.274 0 2.317-1.038 2.317-2.318s-1.044-2.32-2.32-2.32h-9.275v-4.637h9.276c1.276 0 2.32-1.04 2.32-2.32 0-1.28-1.043-2.318-2.318-2.318h-9.275V72.76h9.276zm-23.19 44.058c0 1.275-1.042 2.318-2.318 2.318H64.178c-1.28 0-2.32-1.043-2.32-2.318V70.44c0-1.28 1.04-2.318 2.32-2.318h46.376c1.276 0 2.32 1.04 2.32 2.32v46.376z" id="diagram-sw"/><path d="M108.278 65.717c-1.32 0-2.573.21-3.84.388-4.86-8.59-13.847-14.33-24.046-14.33-10.415 0-19.402 5.806-24.194 14.29-1.227-.164-2.452-.348-3.69-.348-15.373 0-27.887 12.506-27.887 27.885 0 15.387 12.52 27.886 27.89 27.886h55.77c15.374 0 27.888-12.5 27.888-27.886 0-15.38-12.514-27.885-27.886-27.885zm0 41.828h-55.77c-7.693 0-13.944-6.256-13.944-13.943 0-7.645 6.766-13.54 13.956-13.752.028 3.29.6 6.516 1.742 9.565l13.06-4.888c-.587-1.544-.872-3.185-.872-4.867 0-7.687 6.25-13.943 13.942-13.943 4.508 0 8.593 2.233 11.152 5.712-6.727 5.09-11.152 13.09-11.152 22.17h13.943c0-7.69 6.25-13.945 13.943-13.945 7.692 0 13.943 6.256 13.943 13.942s-6.25 13.942-13.94 13.942z" fill="#010101" id="diagram-cloud"/><path d="M124.43 46.652c-.07-2.23-1.943-4.037-4.176-4.037h-9.773s-23.56.524-31.42.52c-7.19-.007-28.754-.52-28.754-.52H40.53c-2.232 0-4.107 1.808-4.176 4.037-.072 2.237 1.698 4.06 3.944 4.06h3.058c.963 5.987 8.793 54.57 12.148 71.708.53 2.7 2.134 5.85 4.943 6.154 1.506.39 8.56 2.073 18.68 2.073 10.334 0 19.235-1.746 20.77-2.064 2.85-.247 4.49-3.428 5.034-6.153 3.438-17.2 11.51-65.79 12.498-71.716h3.063c2.244 0 4.015-1.825 3.944-4.062zm-20.523 4.222l-2.05 2.724c-.75-.87-1.502-1.753-2.253-2.643l4.303-.08zm-12.307.683c2.02 2.61 4.058 5.132 6.112 7.57l-1.15 1.538c-2.04 2.727-4.073 5.447-6.118 8.137-2.157-2.592-4.215-5.198-6.17-7.768 2.412-3.004 4.852-6.16 7.326-9.477zm-15.54 34.86c-2.182 2.406-4.413 4.72-6.708 6.92-2.328-2.552-4.628-5.175-6.9-7.87 2.314-2.21 4.54-4.498 6.68-6.828 2.232 2.623 4.543 5.222 6.927 7.772zM73.79 73.36l.343-.407c2.064-2.28 4.14-4.662 6.233-7.156 2.113 2.52 4.212 4.93 6.298 7.232l.274.322c-2.16 2.77-4.348 5.485-6.572 8.12-2.227-2.634-4.414-5.348-6.575-8.117zm6.575 17.51c2.29 2.292 4.642 4.53 7.07 6.69-2.33 2.443-4.686 4.817-7.07 7.124-2.386-2.307-4.744-4.68-7.073-7.122 2.427-2.16 4.78-4.4 7.073-6.693zm4.307-4.455c2.384-2.55 4.695-5.15 6.928-7.777 2.138 2.328 4.364 4.616 6.676 6.827-2.27 2.693-4.57 5.315-6.898 7.867-2.295-2.198-4.526-4.512-6.706-6.918zm-6.197-35.18c1.35.003 3.137-.01 5.18-.032-.433.607-.862 1.205-1.288 1.794-.65.91-1.32 1.838-2 2.773-.68-.934-1.354-1.862-2.005-2.772-.42-.58-.842-1.17-1.27-1.766.503.003.968.005 1.383.005zm-9.343.32c2.476 3.32 4.915 6.474 7.324 9.478-1.954 2.57-4.013 5.177-6.17 7.77-2.046-2.69-4.08-5.412-6.118-8.14l-1.15-1.536c2.056-2.437 4.095-4.958 6.114-7.57zm-8.026-.58c-.745.883-1.49 1.758-2.236 2.622l-2.037-2.71c1.34.03 2.784.058 4.273.088zm-11.916 1.44c1.532 2.195 3.127 4.446 4.774 6.727-1.032 1.138-2.064 2.257-3.098 3.362-.705-4.22-1.285-7.723-1.676-10.09zm5.02 32.85l-.425-2.327.97 1.715-.545.612zm-1.936-14.358c1.956-1.977 3.897-4.02 5.82-6.142 2.334 3.113 4.77 6.257 7.31 9.386-2.254 2.36-4.502 4.596-6.752 6.718-2.023-2.507-6.253-9.216-6.378-9.957zm5.49 31.98c-.65-3.675-1.324-7.556-2-11.478.637-.528 1.27-1.05 1.895-1.59 2.34 2.662 4.72 5.22 7.102 7.67-2.257 1.92-4.588 3.723-7.002 5.393zm3.975 17.767s-.3.398-.57.68c-.02-.08-.04-.155-.054-.23-.383-1.906-.824-4.226-1.31-6.846 1.537 1.445 3.086 2.87 4.647 4.275-.92.724-1.83 1.432-2.72 2.12zm4.81 2.96c.64-.505 1.28-1.013 1.92-1.526.8.696 1.6 1.39 2.41 2.077-1.64-.163-3.09-.356-4.33-.552zm2.64-8.853c-2.62-2.53-5.198-5.112-7.738-7.752 2.49-1.76 4.91-3.618 7.27-5.55 2.535 2.473 5.067 4.83 7.573 7.076-1.767 1.633-3.55 3.23-5.345 4.79-.592.486-1.18.967-1.765 1.442zm11.7 9.8c-.346.007-.695.01-1.044.015-2.32-2.03-4.614-4.1-6.876-6.213.758-.644 1.514-1.293 2.267-1.95 1.683-1.382 3.393-2.82 5.125-4.305 1.745 1.5 3.47 2.947 5.166 4.342.74.642 1.48 1.277 2.223 1.91-2.256 2.107-4.543 4.172-6.86 6.2zm9.22-.62c.72-.61 1.433-1.23 2.147-1.85.566.458 1.135.91 1.705 1.36-1.136.165-2.432.335-3.85.49zm1.424-9.176c-.57-.46-1.146-.93-1.722-1.406-1.808-1.573-3.604-3.178-5.383-4.823 2.505-2.244 5.036-4.602 7.57-7.074 2.36 1.93 4.78 3.79 7.27 5.55-2.54 2.64-5.117 5.226-7.734 7.755zm7.786 6.315c-.185-.22-.334-.42-.334-.42-.893-.69-1.8-1.397-2.72-2.12 1.452-1.308 2.896-2.633 4.327-3.976-.47 2.48-.9 4.687-1.273 6.514zm3.456-18.32c-2.348-1.633-4.617-3.394-6.815-5.266 2.386-2.45 4.764-5.003 7.11-7.67.58.5 1.167.99 1.757 1.48-.694 3.913-1.385 7.784-2.05 11.457zm3.096-17.49l-.545-.613.97-1.715-.425 2.328zm-3.8-4.402c-2.25-2.122-4.497-4.356-6.75-6.71 2.54-3.13 4.977-6.276 7.312-9.39 1.906 2.105 3.83 4.13 5.77 6.092-.138.793-4.34 7.536-6.333 10.006zm7.775-18.37c-1.03-1.102-2.058-2.216-3.085-3.35 1.666-2.303 3.276-4.577 4.823-6.794-.405 2.366-1.008 5.89-1.74 10.144z" id="diagram-bin"/><path d="M114.31 61.395L98.25 45.298c-1.713-1.717-4.04-2.683-6.465-2.683h-46.3c-5.044 0-9.133 4.09-9.133 9.133v72.49c0 5.044 4.09 9.133 9.132 9.133h62.36c5.043 0 9.133-4.084 9.133-9.13V67.848c-.002-2.418-.96-4.737-2.667-6.45zm-6.467 62.844h-62.36V51.743h46.303l16.058 16.098v56.393zM54.875 55.475c-2.522 0-4.566 2.045-4.566 4.566V72.03c0 2.522 2.04 4.567 4.56 4.567 2.523 0 4.567-2.045 4.567-4.567V60.043c0-2.52-2.044-4.566-4.566-4.566zm13.89 0c-2.522 0-4.567 2.045-4.567 4.566V72.03c0 2.522 2.045 4.567 4.567 4.567 2.522 0 4.566-2.045 4.566-4.567V60.043c0-2.52-2.04-4.566-4.56-4.566zm13.888 0c-2.52 0-4.566 2.045-4.566 4.566V72.03c0 2.522 2.045 4.567 4.566 4.567 2.522 0 4.567-2.045 4.567-4.567V60.043c0-2.52-2.045-4.566-4.567-4.566zm13.89 21.12c2.522 0 4.567-2.044 4.567-4.566v-3.99c0-2.522-2.045-4.567-4.567-4.567-2.522 0-4.566 2.045-4.566 4.567v3.995c0 2.524 2.044 4.57 4.566 4.57z" id="diagram-cache"/><path d="M127.128 135.19H36.996V45.056h90.132v90.132zm-82.774-7.36h75.416V52.416H44.354v75.416zm20.233-53.342h7.358v57.022h-7.358zM40.675 68.97h82.774v7.357H40.67zM66.427 98.4h57.022v7.358H66.422z" id="diagram-template"/><path d="M36.352 68.593v55.405c0 7.533 6.1 13.633 13.627 13.633h64.15c7.532 0 13.64-6.094 13.64-13.63V68.595c0-7.523-6.107-13.626-13.64-13.626h-2.796v-8.202c0-2.293-1.86-4.15-4.15-4.15s-4.155 1.857-4.155 4.15v16.7c0 2.05-1.657 3.705-3.703 3.705-2.037 0-3.687-1.656-3.687-3.704v-8.5h-7.19v-8.2c0-2.294-1.86-4.15-4.15-4.15-2.3 0-4.15 1.856-4.15 4.15v16.7c0 2.048-1.65 3.704-3.703 3.704-2.037 0-3.688-1.658-3.688-3.706v-8.5h-7.1v-8.2c0-2.294-1.865-4.15-4.15-4.15-2.3 0-4.156 1.856-4.156 4.15v16.7c0 2.048-1.652 3.704-3.7 3.704-2.042 0-3.693-1.657-3.693-3.705v-8.5c-7.515.01-13.61 6.105-13.61 13.627zm81.503 53.72c0 2.425-1.966 4.39-4.39 4.39h-62.23c-2.424 0-4.395-1.965-4.395-4.39v-35.72h71.015v35.723zm-4.947-29.247v5.54h-3.218v22.145h-5.42V98.61h-3.207v-5.54h11.845m-13.08 0v27.685h-4.752l-2.82-12.585v12.586h-4.53v-27.69h4.53l3.04 12.467V93.066h4.53m-23.07 0h9.037v5.54h-3.618v5.25h3.387v5.267h-3.387v6.087h3.98v5.54h-9.4V93.067m-.927 0l-2.755 27.685h-8.228l-3.128-27.684h5.716c.66 7.626 1.133 14.073 1.417 19.34.283-5.324.58-10.055.888-14.193l.373-5.147h5.72m-23.843 0h9.04v5.54h-3.62v5.25h3.387v5.267H57.41v6.087h3.977v5.54h-9.4V93.067" id="diagram-event"/></defs></svg></p>
  456. <div class="diagram-container"><svg viewbox="0 0 549.92853 342.97787"><g transform="translate(-36.352 -42.615)"><text x="100.277" y="188.362" class="label">ServiceWorker</text><text x="315.902" y="189.362" class="label">install</text><text x="538" y="186.362" class="label">activate</text><text y="379.362" x="216.277" class="label">Cache</text><text x="426.277" y="381.362" class="label">Network</text><use xlink:href="#diagram-sw" transform="translate(10 9)"/><use transform="translate(236 13)" xlink:href="#diagram-event"/><use xlink:href="#diagram-event" transform="translate(456 13)"/><use transform="translate(343 209)" xlink:href="#diagram-network"/><use xlink:href="#diagram-cache" transform="translate(139 209)"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M159 105.362h99"/><path d="M248.617 100.633l10.898 4.51-10.857 5"/></g><text x="208.128" y="96.32" class="fade-in state-label">1</text></g><g class="anim"><g class="joiner"><path d="M372.872 129.49s105.32 29.255 76.064 119.15"/></g><g class="joiner"><path d="M372.872 136.937s94.68 27.66 69.68 109.042"/></g><g class="joiner"><path d="M371.29 144.396s89.343 27.658 63.812 101.063l-3.8-6.91"/></g><g class="joiner"><path d="M372.34 122.043s116.49 28.723 82.98 130.32l6.313-2.927"/></g><text x="410.128" y="204.32" class="fade-in state-label">2</text></g><g class="anim"><text y="270.32" x="318.128" class="fade-in state-label">3</text><g class="joiner"><path d="M373.795 292.362s-34.134-1.255-52.1-1.255S266 292.362 266 292.362"/></g><g class="joiner"><path d="M373.795 298.362s-34.134 1.38-52.1 1.38-55.695-1.38-55.695-1.38"/></g><g class="joiner"><path d="M373.795 286.362s-34.134-4-52.1-4-55.695 4-55.695 4l5.37-5.81"/></g><g class="joiner"><path d="M373.795 304.362s-34.134 4-52.1 4-55.695-4-55.695-4l5.535 6.244"/></g></g><g class="anim"><text y="96.32" x="428.128" class="fade-in state-label">4</text><g class="joiner"><path d="M372 103.362h108"/><path d="M468.617 98.633l10.898 4.51-10.857 5"/></g></g></g></g></svg></div>
  457. <p>ServiceWorker gives you an install event. You can use this to get stuff ready, stuff that must be ready before you handle other events. While this happens any previous version of your ServiceWorker is still running &amp; serving pages, so the things you do here mustn't disrupt that.</p>
  458. <p><strong>Ideal for:</strong> CSS, images, fonts, JS, templates… basically anything you'd consider static to that "version" of your site.</p>
  459. <p>These are things that would make your site entirely non-functional if they failed to fetch, things an equivalent native-app would make part of the initial download.</p>
  460. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'install'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  461. <span class="nx">event</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span>
  462. <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'mysite-static-v3'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">cache</span><span class="p">)</span> <span class="p">{</span>
  463. <span class="k">return</span> <span class="nx">cache</span><span class="p">.</span><span class="nx">addAll</span><span class="p">([</span>
  464. <span class="s1">'/css/whatever-v3.css'</span><span class="p">,</span>
  465. <span class="s1">'/css/imgs/sprites-v6.png'</span><span class="p">,</span>
  466. <span class="s1">'/css/fonts/whatever-v8.woff'</span><span class="p">,</span>
  467. <span class="s1">'/js/all-min-v4.js'</span>
  468. <span class="c1">// etc</span>
  469. <span class="p">]);</span>
  470. <span class="p">})</span>
  471. <span class="p">);</span>
  472. <span class="p">});</span>
  473. </pre></div>
  474. <p><code>event.waitUntil</code> takes a promise to define the length &amp; success of the install. If the promise rejects, the installation is considered a failure and this ServiceWorker will be abandoned (if an older version is running, it'll be left intact). <code>caches.open</code> and <code>cache.addAll</code> return promises. If any of the resources fail to fetch, the <code>cache.addAll</code> call rejects.</p>
  475. <p>On <a href="https://jakearchibald.github.io/trained-to-thrill/">trained-to-thrill</a> I use this to <a href="https://github.com/jakearchibald/trained-to-thrill/blob/3291dd40923346e3cc9c83ae527004d502e0464f/www/static/js-unmin/sw/index.js#L3">cache static assets</a>.</p>
  476. <h3 id="on-install-not-as-a-dependency">On install - not as a dependency</h3>
  477. <div class="diagram-container"><svg viewbox="0 0 549.92853 342.97787"><g transform="translate(-36.352 -42.615)"><text x="100.277" y="188.362" class="label">ServiceWorker</text><text x="315.902" y="189.362" class="label">install</text><text x="538" y="186.362" class="label">activate</text><text y="379.362" x="216.277" class="label">Cache</text><text x="426.277" y="381.362" class="label">Network</text><use xlink:href="#diagram-sw" transform="translate(10 9)"/><use transform="translate(236 13)" xlink:href="#diagram-event"/><use xlink:href="#diagram-event" transform="translate(456 13)"/><use transform="translate(343 209)" xlink:href="#diagram-network"/><use xlink:href="#diagram-cache" transform="translate(139 209)"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M159 105.362h99"/><path d="M248.617 100.633l10.898 4.51-10.857 5"/></g><text x="208.128" y="96.32" class="fade-in state-label">1</text></g><g class="anim"><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M372.872 129.49s105.32 29.255 76.064 119.15"/></g><g class="joiner"><path d="M372.872 136.937s94.68 27.66 69.68 109.042"/></g><g class="joiner"><path d="M371.29 144.396s89.343 27.658 63.812 101.063l-3.8-6.91"/></g><g class="joiner"><path d="M372.34 122.043s116.49 28.723 82.98 130.32l6.313-2.927"/></g><text x="410.128" y="204.32" class="fade-in state-label">2</text></g><g class="anim"><text y="270.32" x="318.128" class="fade-in state-label">3</text><g class="joiner"><path d="M373.795 292.362s-34.134-1.255-52.1-1.255S266 292.362 266 292.362"/></g><g class="joiner"><path d="M373.795 298.362s-34.134 1.38-52.1 1.38-55.695-1.38-55.695-1.38"/></g><g class="joiner"><path d="M373.795 286.362s-34.134-4-52.1-4-55.695 4-55.695 4l5.37-5.81"/></g><g class="joiner"><path d="M373.795 304.362s-34.134 4-52.1 4-55.695-4-55.695-4l5.535 6.244"/></g></g></g><g class="anim"><text class="fade-in state-label" x="428.128" y="96.32">2</text><g class="joiner"><path d="M372 103.362h108"/><path d="M468.617 98.633l10.898 4.51-10.857 5"/></g></g></g></g></g></svg></div>
  478. <p>Similar to above, but won't delay install completing and won't cause installation to fail if caching fails.</p>
  479. <p><strong>Ideal for:</strong> Bigger resources that aren't needed straight away, such as assets for later levels of a game.</p>
  480. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'install'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  481. <span class="nx">event</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span>
  482. <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'mygame-core-v1'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">cache</span><span class="p">)</span> <span class="p">{</span>
  483. <span class="nx">cache</span><span class="p">.</span><span class="nx">addAll</span><span class="p">(</span>
  484. <span class="c1">// levels 11-100</span>
  485. <span class="p">);</span>
  486. <span class="k">return</span> <span class="nx">cache</span><span class="p">.</span><span class="nx">addAll</span><span class="p">(</span>
  487. <span class="c1">// core assets &amp; levels 1-10</span>
  488. <span class="p">);</span>
  489. <span class="p">})</span>
  490. <span class="p">);</span>
  491. <span class="p">});</span>
  492. </pre></div>
  493. <p>We're not passing the <code>cache.addAll</code> promise for levels 11-100 back to <code>event.waitUntil</code>, so even if it fails, the game will still be available offline. Of course, you'll have to cater for the possible absence of those levels &amp; reattempt caching them if they're missing.</p>
  494. <p>Also, the ServiceWorker may be killed while levels 11-100 download since it's finished handling events, but the download will continue in the background.</p>
  495. <h3 id="on-activate">On activate</h3>
  496. <div class="diagram-container"><svg viewbox="0 0 549.92853 342.97787"><g transform="translate(-36.352 -42.615)"><text x="100.277" y="188.362" class="label">ServiceWorker</text><text x="315.902" y="189.362" class="label">install</text><text x="538" y="186.362" class="label">activate</text><text y="379.362" x="216.277" class="label">Deleted!!</text><text x="424.277" y="381.362" class="label">Cache</text><use xlink:href="#diagram-sw" transform="translate(10 9)"/><use transform="translate(236 13)" xlink:href="#diagram-event"/><use xlink:href="#diagram-event" transform="translate(456 13)"/><use transform="translate(347 209)" xlink:href="#diagram-cache"/><use xlink:href="#diagram-bin" transform="translate(135 209)"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M159 105.362h99"/><path d="M248.617 100.633l10.898 4.51-10.857 5"/></g><text x="208.128" y="96.32" class="fade-in state-label">1</text></g><g class="anim"><text class="fade-in state-label" x="428.128" y="96.32">2</text><g class="joiner"><path d="M372 103.362h108"/><path d="M468.617 98.633l10.898 4.51-10.857 5"/></g></g><g class="anim"><g class="joiner"><path d="M481.872 127.937c-45 0-61.566 28.956-60.566 112.34"/><path d="M416.588 232.76l4.925 10.583 5.108-11.08"/></g><text x="445.128" y="193.32" class="fade-in state-label">3</text></g><g class="anim"><text y="263.334" x="342.301" class="fade-in state-label">4</text><g class="joiner"><path d="M373.795 286.362c-54.104 1.072-78.643-100.474-128.02-101.53-49.38-1.053-49.414 57.604-49.414 57.604l-4.807-6.882"/></g><g class="joiner"><path d="M373.795 304.362c-61.068 1.607-100.07-101.01-128.557-100.457-28.486.552-29.593 39.925-29.593 39.925l5.37-6.882"/></g><g class="joiner"><path d="M373.795 298.362c-59.997 0-92.57-100.474-128.02-100.993-35.452-.52-36.558 47.423-36.558 47.423"/></g><g class="joiner"><path d="M373.795 292.362c-57.318.536-84.536-100.474-128.02-100.993-43.487-.52-42.986 53.316-42.986 53.316"/></g></g></g></g></svg></div>
  497. <p><strong>Ideal for:</strong> Clean-up &amp; migration.</p>
  498. <p>Once a new ServiceWorker has installed &amp; a previous version isn't being used, the new one activates, and you get an <code>activate</code> event. Because the old version is out of the way, it's a good time to handle schema migrations in IndexedDB and also delete unused caches. </p>
  499. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'activate'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  500. <span class="nx">event</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span>
  501. <span class="nx">caches</span><span class="p">.</span><span class="nx">keys</span><span class="p">().</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">cacheNames</span><span class="p">)</span> <span class="p">{</span>
  502. <span class="k">return</span> <span class="nx">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span>
  503. <span class="nx">cacheNames</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">cacheName</span><span class="p">)</span> <span class="p">{</span>
  504. <span class="c1">// Return true if you want to remove this cache,</span>
  505. <span class="c1">// but remember that caches are shared across</span>
  506. <span class="c1">// the whole origin</span>
  507. <span class="p">}).</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">cacheName</span><span class="p">)</span> <span class="p">{</span>
  508. <span class="k">return</span> <span class="nx">caches</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">cacheName</span><span class="p">);</span>
  509. <span class="p">})</span>
  510. <span class="p">);</span>
  511. <span class="p">})</span>
  512. <span class="p">);</span>
  513. <span class="p">});</span>
  514. </pre></div>
  515. <p>During activation, other events such as <code>fetch</code> are put into a queue, so a long activation could potentially block page loads. Keep your activation as lean as possible, only use it for things you <em>couldn't</em> do while the old version was active.</p>
  516. <p>On <a href="https://jakearchibald.github.io/trained-to-thrill/">trained-to-thrill</a> I use this to <a href="https://github.com/jakearchibald/trained-to-thrill/blob/3291dd40923346e3cc9c83ae527004d502e0464f/www/static/js-unmin/sw/index.js#L17">remove old caches</a>.</p>
  517. <h3 id="on-user-interaction">On user interaction</h3>
  518. <p><strong>Ideal for</strong>: If the whole site can't be taken offline, you may allow the user to select the content they want available offline. E.g. a video on something like YouTube, an article on Wikipedia, a particular gallery on Flickr.</p>
  519. <p>Give the user a "Read later" or "Save for offline" button. When it's clicked, fetch what you need from the network &amp; pop it in the cache.</p>
  520. <div class="codehilite"><pre><span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">'.cache-article'</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  521. <span class="nx">event</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
  522. <span class="kd">var</span> <span class="nx">id</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">dataset</span><span class="p">.</span><span class="nx">articleId</span><span class="p">;</span>
  523. <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'mysite-article-'</span> <span class="o">+</span> <span class="nx">id</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">cache</span><span class="p">)</span> <span class="p">{</span>
  524. <span class="nx">fetch</span><span class="p">(</span><span class="s1">'/get-article-urls?id='</span> <span class="o">+</span> <span class="nx">id</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  525. <span class="c1">// /get-article-urls returns a JSON-encoded array of</span>
  526. <span class="c1">// resource URLs that a given article depends on</span>
  527. <span class="k">return</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
  528. <span class="p">}).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">urls</span><span class="p">)</span> <span class="p">{</span>
  529. <span class="nx">cache</span><span class="p">.</span><span class="nx">addAll</span><span class="p">(</span><span class="nx">urls</span><span class="p">);</span>
  530. <span class="p">});</span>
  531. <span class="p">});</span>
  532. <span class="p">});</span>
  533. </pre></div>
  534. <p>The above doesn't work in Chrome yet, as we've yet to expose <code>fetch</code> and <code>caches</code> to pages (<a href="https://code.google.com/p/chromium/issues/detail?id=436770">ticket #1</a>, <a href="https://code.google.com/p/chromium/issues/detail?id=439389">ticket #2</a>). You could use <code>navigator.serviceWorker.controller.postMessage</code> to <a href="https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker/post-message">send a signal to the ServiceWorker</a> telling it which article to cache, and handle the rest there.</p>
  535. <h3 id="on-network-response">On network response</h3>
  536. <div class="diagram-container"><svg viewbox="0 0 549.92853 265"><g transform="translate(-36.352 -120.593)"><text x="248.603" y="384.105" class="label">ServiceWorker</text><use xlink:href="#diagram-sw" transform="translate(160.325 204.743)"/><text x="89.548" y="252.972" class="label">Page</text><use transform="translate(-31.309 48.726)" xlink:href="#diagram-page"/><text x="537.439" y="383.491" class="label">Network</text><use xlink:href="#diagram-network" transform="translate(455.439 210.129)"/><text x="400.625" y="253.272" class="label">Cache</text><use transform="translate(323.347 80.91)" xlink:href="#diagram-cache"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M125.228 217.424c-.572 41.786 20.007 74.986 60.648 75.56"/><path d="M177.64 287.113l10.897 5.65-10.857 5.002"/></g><text x="168.128" y="196.32" class="fade-in state-label" transform="translate(-33.772 202.062) translate(0 -90)">1</text></g><g class="anim"><g class="joiner"><path d="M304.587 319.783c42.535 26.945 131.032 32.45 186.58 10.876"/><path d="M480.67 328.615l12.032 1.294-8.38 8.604"/></g><text class="fade-in state-label" y="201.45" x="213.165" transform="translate(-33.772 202.062) translate(220 -30)">2</text></g><g class="anim"><g class="joiner"><path d="M485.305 298.69c-256.192-4.98-84.462-128.887-340.756-128.798"/><path d="M152.65 164.397l-9.692 5.7 9.692 5.132"/></g><text x="116.819" y="71.469" class="fade-in state-label" transform="translate(-33.772 202.062) translate(210.879 -89.29)">3</text><g class="joiner"><path d="M485.306 298.69c-119.805-2.33-146.03-30.666-166.21-60.167-22.97-33.584-7.32-70.388 30.117-70.34"/><path d="M340.78 164.397l9.692 4.56-10.262 4.562"/></g></g></g></g></svg></div>
  537. <p><strong>Ideal for</strong>: Frequently updating resources such as a user's inbox, or article contents. Also useful for non-essential content such as avatars, but care is needed.</p>
  538. <p>If a request doesn't match anything in the cache, get it from the network, send it to the page &amp; add it to the cache at the same time.</p>
  539. <p>If you do this for a range of URLs, such as avatars, you'll need to be careful you don't bloat the storage of your origin — if the user needs to reclaim disk space you don't want to be the prime candidate. Make sure you get rid of items in the cache you don't need any more.</p>
  540. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'fetch'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  541. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span>
  542. <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'mysite-dynamic'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">cache</span><span class="p">)</span> <span class="p">{</span>
  543. <span class="k">return</span> <span class="nx">cache</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  544. <span class="k">return</span> <span class="nx">response</span> <span class="o">||</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">clone</span><span class="p">()).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  545. <span class="nx">cache</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">,</span> <span class="nx">response</span><span class="p">.</span><span class="nx">clone</span><span class="p">());</span>
  546. <span class="k">return</span> <span class="nx">response</span><span class="p">;</span>
  547. <span class="p">});</span>
  548. <span class="p">});</span>
  549. <span class="p">})</span>
  550. <span class="p">);</span>
  551. <span class="p">});</span>
  552. </pre></div>
  553. <p>To allow for efficient memory usage, you can only read a response/request's body once. In the code above, <a href="https://fetch.spec.whatwg.org/#dom-request-clone"><code>.clone()</code></a> is used to create additional copies that can be read separately.</p>
  554. <p>On <a href="https://jakearchibald.github.io/trained-to-thrill/">trained-to-thrill</a> I use this to <a href="https://github.com/jakearchibald/trained-to-thrill/blob/3291dd40923346e3cc9c83ae527004d502e0464f/www/static/js-unmin/sw/index.js#L109">cache Flickr images</a>.</p>
  555. <h3 id="stale-while-revalidate">Stale-while-revalidate</h3>
  556. <div class="diagram-container"><svg viewbox="0 0 549.92853 265"><g transform="translate(-36.352 -120.593)"><text x="248.603" y="384.105" class="label">ServiceWorker</text><use xlink:href="#diagram-sw" transform="translate(160.325 204.743)"/><text x="89.548" y="252.972" class="label">Page</text><use transform="translate(-31.309 48.726)" xlink:href="#diagram-page"/><text x="537.439" y="383.491" class="label">Network</text><use xlink:href="#diagram-network" transform="translate(455.439 210.129)"/><text x="400.625" y="253.272" class="label">Cache</text><use transform="translate(323.347 80.91)" xlink:href="#diagram-cache"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M125.228 217.424c-.572 41.786 20.007 74.986 60.648 75.56"/><path d="M177.64 287.113l10.897 5.65-10.857 5.002"/></g><text x="189.791" y="151.852" class="fade-in state-label" transform="translate(-33.772 202.062) translate(0 -90)">1</text></g><g class="anim"><g class="joiner"><path d="M306.297 294.13c53.367 3.57 53.5-42.803 58.308-70.08"/><path d="M358.1 231.7l6.902-10.108 3.593 12.025"/></g><text class="fade-in state-label" y="91.993" x="145.324" transform="translate(-33.772 202.062) translate(220 -30)">2</text></g><g class="anim"><g class="joiner"><path d="M349.054 172.7c-48.11-26.075-154.585-24.56-198.804-3.377"/><path d="M156.36 160.117l-9.12 9.69 11.97 1.712"/></g><text x="145.324" y="91.993" class="fade-in state-label" transform="translate(-33.772 202.062) translate(129.355 -98.981)">3</text></g><g class="anim"><g class="joiner"><path d="M305.727 320.923c63.06 22.954 117.92 22.76 182.588 6.315"/><path d="M477.033 324.593l15.393 1.14-11.972 9.692"/></g><text x="215.445" y="131.329" class="fade-in state-label" transform="translate(-33.772 202.062) translate(219.43 -3.206)">4</text></g><g class="anim"><g class="joiner"><path d="M534.613 243.64c-1.14-30.785-30.215-70.12-83.804-70.69"/><path d="M459.93 167.248l-11.4 6.27 11.4 5.132"/></g><text class="fade-in state-label" y="149.572" x="215.445" transform="translate(-33.772 202.062) translate(312.925 -130.336)">5</text></g></g></g></svg></div>
  557. <p><strong>Ideal for</strong>: Frequently updating resources where having the very latest version is non-essential. Avatars can fall into this category.</p>
  558. <p>If there's a cached version available, use it, but fetch an update for next time.</p>
  559. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'fetch'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  560. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span>
  561. <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'mysite-dynamic'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">cache</span><span class="p">)</span> <span class="p">{</span>
  562. <span class="k">return</span> <span class="nx">cache</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  563. <span class="kd">var</span> <span class="nx">fetchPromise</span> <span class="o">=</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">clone</span><span class="p">()).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">networkResponse</span><span class="p">)</span> <span class="p">{</span>
  564. <span class="nx">cache</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">,</span> <span class="nx">networkResponse</span><span class="p">.</span><span class="nx">clone</span><span class="p">());</span>
  565. <span class="k">return</span> <span class="nx">networkResponse</span><span class="p">;</span>
  566. <span class="p">})</span>
  567. <span class="k">return</span> <span class="nx">response</span> <span class="o">||</span> <span class="nx">fetchPromise</span><span class="p">;</span>
  568. <span class="p">})</span>
  569. <span class="p">})</span>
  570. <span class="p">);</span>
  571. <span class="p">});</span>
  572. </pre></div>
  573. <p>This is very similar to HTTP's <a href="https://www.mnot.net/blog/2007/12/12/stale">stale-while-revalidate</a>.</p>
  574. <h3 id="on-push-message">On push message</h3>
  575. <div class="diagram-container"><svg viewbox="0 0 549.92853 342.97787"><g transform="translate(-36.352 -42.615)"><text x="100.277" y="188.362" class="label">Push Message</text><text x="321.902" y="189.362" class="label">ServiceWorker</text><text x="538" y="186.362" class="label">Network</text><text y="379.362" x="216.277" class="label">Notification</text><text x="424.277" y="381.362" class="label">Cache</text><use xlink:href="#diagram-cloud" transform="translate(16.971 15.971)"/><use transform="translate(233.286 11.257)" xlink:href="#diagram-sw"/><use xlink:href="#diagram-network" transform="translate(454.257 13)"/><use transform="translate(347 209)" xlink:href="#diagram-cache"/><use xlink:href="#diagram-notification" transform="translate(135 209)"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M154.643 84.45h103"/><path d="M248.26 79.72l10.9 4.51-10.858 5"/></g><text x="218.128" y="94.32" class="fade-in state-label" transform="translate(-6.971 127.219) translate(-1.386 -148.131)">1</text></g><g class="anim"><text class="fade-in state-label" x="358.572" y="83.597" transform="translate(-6.971 127.219) translate(81.772 -141.141)">2</text><g class="joiner"><path d="M379.793 78.763l102.057.46-4.882-3.546"/></g><g class="joiner"><path d="M379.793 96.763l102.057.46-5.256 3.436"/></g><g class="joiner"><path d="M379.793 90.763l102.057.46"/></g><g class="joiner"><path d="M379.793 84.763l102.057.46"/></g></g><g class="anim"><g class="joiner"><path d="M487.095 112.714c-87.967 6.467-78.01 129.51-78.01 129.51l-5.228-6.838"/></g><g class="joiner"><path d="M495.443 130.41c-75.546 1.314-68.358 111.813-68.358 111.813l4.022-6.434"/></g><g class="joiner"><path d="M492.258 124.78c-78.995 1.655-71.173 117.443-71.173 117.443"/></g><g class="joiner"><path d="M489.073 118.747c-82.676 3.836-73.988 123.476-73.988 123.476"/></g><text y="191.776" x="455.732" class="fade-in state-label" transform="translate(-6.971 127.219) translate(7.441 -127.441)">3</text></g><g class="anim"><text class="fade-in state-label" y="145.264" x="219.272" transform="translate(-6.971 127.219) translate(-10.978 -84.94)">4</text><g class="joiner"><path d="M262.968 124.172c-57.24 1.717-45.827 116.772-45.827 116.772"/><path d="M211.077 231.84l5.724 11.448 5.154-11.448"/></g></g></g></g></svg></div>
  576. <p><strong>Note:</strong> Push isn't supported in Chrome yet.</p>
  577. <p>The <a href="https://w3c.github.io/push-api/">Push API</a> is another feature built on top of SerivceWorker. This allows the ServiceWorker to be awoken in response to a message from the OS's messaging service. This happens even when the user doesn't have a tab open to your site, only the ServiceWorker is woken up. You request permission to do this from a page &amp; the user will be prompted.</p>
  578. <p><strong>Ideal for</strong>: Content relating to a notification, such as a chat message, a breaking news story, or an email. Also infrequently changing content that benefits from immediate sync, such as a todo list update or a calendar alteration.</p>
  579. <p>The common final outcome is a notification which, when tapped, opens/focuses a relevant page, but updating caches before this happens is <em>extremely</em> important. The user is obviously online at the time of receiving the push message, but they may not be when they finally interact with the notification, so making this content available offline is important. The Twitter native app, which is for the most part an excellent example of offline-first, gets this a bit wrong:</p>
  580. <figure class="full-figure">
  581. <p class="video"/>
  582. <figcaption>Twitter notification while offline</figcaption>
  583. </figure>
  584. <p>Without a connection, Twitter fails to provide the content relating to the push message. Tapping it does remove the notification however, leaving the user with less information than before they tapped. Don't do this!</p>
  585. <p>This code updates caches before showing a notification:</p>
  586. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'push'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  587. <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">text</span><span class="p">()</span> <span class="o">==</span> <span class="s1">'new-email'</span><span class="p">)</span> <span class="p">{</span>
  588. <span class="nx">event</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span>
  589. <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'mysite-dynamic'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">cache</span><span class="p">)</span> <span class="p">{</span>
  590. <span class="k">return</span> <span class="nx">fetch</span><span class="p">(</span><span class="s1">'/inbox.json'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  591. <span class="nx">cache</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="s1">'/inbox.json'</span><span class="p">,</span> <span class="nx">response</span><span class="p">.</span><span class="nx">clone</span><span class="p">());</span>
  592. <span class="k">return</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
  593. <span class="p">});</span>
  594. <span class="p">}).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">emails</span><span class="p">)</span> <span class="p">{</span>
  595. <span class="nx">registration</span><span class="p">.</span><span class="nx">showNotification</span><span class="p">(</span><span class="s2">"New email"</span><span class="p">,</span> <span class="p">{</span>
  596. <span class="nx">body</span><span class="o">:</span> <span class="s2">"From "</span> <span class="o">+</span> <span class="nx">emails</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">from</span><span class="p">.</span><span class="nx">name</span>
  597. <span class="nx">tag</span><span class="o">:</span> <span class="s2">"new-email"</span>
  598. <span class="p">});</span>
  599. <span class="p">})</span>
  600. <span class="p">);</span>
  601. <span class="p">}</span>
  602. <span class="p">});</span>
  603. <span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'notificationclick'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  604. <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">notification</span><span class="p">.</span><span class="nx">tag</span> <span class="o">==</span> <span class="s1">'new-email'</span><span class="p">)</span> <span class="p">{</span>
  605. <span class="c1">// Assume that all of the resources needed to render</span>
  606. <span class="c1">// /inbox/ have previously been cached, e.g. as part</span>
  607. <span class="c1">// of the install handler.</span>
  608. <span class="k">new</span> <span class="nx">WindowClient</span><span class="p">(</span><span class="s1">'/inbox/'</span><span class="p">);</span>
  609. <span class="p">}</span>
  610. <span class="p">});</span>
  611. </pre></div>
  612. <h3 id="on-background-sync">On background-sync</h3>
  613. <div class="diagram-container"><svg viewbox="0 0 583.92999 160"><g transform="translate(-36.352 -225.593)"><text x="106.692" y="367.362" class="label">ServiceWorker</text><text x="251.902" y="368.362" class="label">sync</text><text x="408" y="369.362" class="label">Network</text><text x="562.326" y="371.177" class="label">Cache</text><use xlink:href="#diagram-sw" transform="translate(19.106 188.308)"/><use transform="translate(172 188)" xlink:href="#diagram-event"/><use xlink:href="#diagram-network" transform="translate(326 192)"/><use transform="translate(485.049 194.815)" xlink:href="#diagram-cache"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M160.126 280.362h37.956"/><path d="M190.188 275.633l10.898 4.51-10.857 5"/></g><text x="160.128" y="96.32" class="fade-in state-label" transform="translate(18 175)">1</text></g><g class="anim"><text class="fade-in state-label" x="382.572" y="83.597" transform="translate(18 175) translate(-70.315 1.985)">2</text><g class="joiner"><path d="M304.677 269.67l50.057.46-4.882-3.546"/></g><g class="joiner"><path d="M304.677 287.67l50.057.46-5.256 3.436"/></g><g class="joiner"><path d="M304.677 281.67l50.057.46"/></g><g class="joiner"><path d="M304.677 275.67l50.057.46"/></g></g><g class="anim"><text y="83.597" x="382.572" class="fade-in state-label" transform="translate(18 175) translate(85.685 1.985)">3</text><g class="joiner"><path d="M460.677 269.67l50.057.46-4.882-3.546"/></g><g class="joiner"><path d="M460.677 287.67l50.057.46-5.256 3.436"/></g><g class="joiner"><path d="M460.677 281.67l50.057.46"/></g><g class="joiner"><path d="M460.677 275.67l50.057.46"/></g></g></g></g></svg></div>
  614. <p><strong>Note:</strong> Background sync isn't supported in Chrome yet.</p>
  615. <p><a href="https://github.com/slightlyoff/BackgroundSync/blob/master/explainer.md">Background sync</a> is another feature built on top of ServiceWorker. It allows you to request background data synchronisation as a one-off, or on an (extremely heuristic) interval. This happens even when the user doesn't have a tab open to your site, only the ServiceWorker is woken up. You request permission to do this from a page &amp; the user will be prompted.</p>
  616. <p><strong>Ideal for</strong>: Non-urgent updates, especially those that happen so regularly that a push message per update would be too frequent, such as social timelines or news articles.</p>
  617. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'sync'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  618. <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">id</span> <span class="o">==</span> <span class="s1">'update-leaderboard'</span><span class="p">)</span> <span class="p">{</span>
  619. <span class="nx">event</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span>
  620. <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'mygame-dynamic'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">cache</span><span class="p">)</span> <span class="p">{</span>
  621. <span class="k">return</span> <span class="nx">cache</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="s1">'/leaderboard.json'</span><span class="p">);</span>
  622. <span class="p">})</span>
  623. <span class="p">);</span>
  624. <span class="p">}</span>
  625. <span class="p">});</span>
  626. </pre></div>
  627. <h2 id="cache-persistence">Cache persistence</h2>
  628. <p>Your origin is given a certain amount of free space to do what it wants with. That free space is shared between all origin storage: LocalStorage, IndexedDB, Filesystem, and of course Caches.</p>
  629. <p>The amount you get isn't spec'd, it will differ depending on device and storage conditions. You can find out how much you've got via:</p>
  630. <div class="codehilite"><pre><span class="nx">navigator</span><span class="p">.</span><span class="nx">storageQuota</span><span class="p">.</span><span class="nx">queryInfo</span><span class="p">(</span><span class="s2">"temporary"</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">info</span><span class="p">)</span> <span class="p">{</span>
  631. <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">info</span><span class="p">.</span><span class="nx">quota</span><span class="p">);</span>
  632. <span class="c1">// Result: &lt;quota in bytes&gt;</span>
  633. <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">info</span><span class="p">.</span><span class="nx">usage</span><span class="p">);</span>
  634. <span class="c1">// Result: &lt;used data in bytes&gt;</span>
  635. <span class="p">});</span>
  636. </pre></div>
  637. <p>However, like all browser storage, the browser is free to throw it away if the device becomes under storage pressure. Unfortunately the browser can't tell the different between those movies you want to keep at all costs, and the game you don't really care about.</p>
  638. <p>To work around this, there's a proposed API, <a href="https://github.com/slightlyoff/StorageDurability/blob/master/explainer.md">Storage Durability</a>:</p>
  639. <div class="codehilite"><pre><span class="c1">// From a page:</span>
  640. <span class="nx">navigator</span><span class="p">.</span><span class="nx">requestStorageDurability</span><span class="p">().</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  641. <span class="c1">// Hurrah, your data is here to stay!</span>
  642. <span class="p">})</span>
  643. </pre></div>
  644. <p>Of course, the user has to grant permission. Making the user part of this flow is important, as we can now expect them to be in control of deletion. If their device comes under storage pressure, and clearing non-essential data doesn't solve it, the user gets to make a judgement call on which items to keep and remove.</p>
  645. <p>For this to work, it requires operating systems to treat "durable" origins as equivalent to native apps in their breakdowns of storage usage, rather than reporting the browser as a single item.</p>
  646. <h2 id="serving-suggestions-responding-to-requests">Serving suggestions - responding to requests</h2>
  647. <p>It doesn't matter how much caching you do, the ServiceWorker won't use the cache unless you tell it when &amp; how. Here are a few patterns for handling requests:</p>
  648. <h3 id="cache-only">Cache only</h3>
  649. <div class="diagram-container"><svg viewbox="0 0 583.92999 198"><g transform="translate(-36.352 -187.593)"><text x="329.228" y="326.179" class="label">ServiceWorker</text><text x="87.057" y="369.32" class="label">Page</text><text x="578.326" y="369.177" class="label">Cache</text><use xlink:href="#diagram-sw" transform="translate(241.641 147.125)"/><use transform="translate(-33.099 166.197)" xlink:href="#diagram-page"/><use transform="translate(501.049 200.815)" xlink:href="#diagram-cache"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M141.816 270.165c39.493 0 68.035-34.507 124.373-33.1"/><path d="M258.498 232.51l10.898 4.51-10.857 5"/></g><text x="172.128" y="56.32" class="fade-in state-label" transform="translate(18 175) translate(0 6)">1</text></g><g class="anim"><g class="joiner"><path d="M390.22 237.024c39.493 0 78.142 35.04 134.48 33.63"/><path d="M517.54 266.02l10.898 4.51-10.857 5"/></g><text class="fade-in state-label" y="65.894" x="197.128" transform="translate(18 175) translate(248.404 6)">2</text></g><g class="anim"><g class="joiner"><path d="M529.97 317.657c-89.42 0-130.307 31.327-199.468 31.327-69.162 0-97.34-35.582-184.044-35.582"/><path d="M154.102 309.13l-10.136 4.44 10.486 4.102"/></g><text x="218.992" y="92.504" class="fade-in state-label" transform="translate(18 175) translate(227.128 90.043)">3</text></g></g></g></svg></div>
  650. <p><strong>Ideal for:</strong> Anything you'd consider static to that "version" of your site. You should have cached these in the install event, so you can depend on them being there.</p>
  651. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'fetch'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  652. <span class="c1">// If a match isn't found in the cache, the response</span>
  653. <span class="c1">// will look like a connection error</span>
  654. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span><span class="nx">caches</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">));</span>
  655. <span class="p">});</span>
  656. </pre></div>
  657. <p>…although you don't often need to handle this case specifically, <a href="#cache-falling-back-to-network">"Cache, falling back to network"</a> covers it.</p>
  658. <h3 id="network-only">Network only</h3>
  659. <div class="diagram-container"><svg viewbox="0 0 583.92999 198"><g transform="translate(-36.352 -187.593)"><text x="329.228" y="326.179" class="label">ServiceWorker</text><text x="87.057" y="369.32" class="label">Page</text><text x="572.326" y="369.177" class="label">Network</text><use xlink:href="#diagram-sw" transform="translate(241.641 147.125)"/><use transform="translate(-33.099 166.197)" xlink:href="#diagram-page"/><use transform="translate(489.049 200.815)" xlink:href="#diagram-network"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M141.816 270.165c39.493 0 68.035-34.507 124.373-33.1"/><path d="M258.498 232.51l10.898 4.51-10.857 5"/></g><text x="172.128" y="56.32" class="fade-in state-label" transform="translate(18 175) translate(0 6)">1</text></g><g class="anim"><g class="joiner"><path d="M390.22 237.024c39.493 0 74.142 35.04 130.48 33.63"/><path d="M511.54 266.02l10.898 4.51-10.857 5"/></g><text class="fade-in state-label" y="65.894" x="197.128" transform="translate(18 175) translate(248.404 6)">2</text></g><g class="anim"><g class="joiner"><path d="M525.97 317.657c-89.42 0-126.307 31.327-195.468 31.327-69.162 0-97.34-35.582-184.044-35.582"/><path d="M154.102 309.13l-10.136 4.44 10.486 4.102"/></g><text x="218.992" y="92.504" class="fade-in state-label" transform="translate(18 175) translate(227.128 90.043)">3</text></g></g></g></svg></div>
  660. <p><strong>Ideal for:</strong> Things that have no offline equivalent, such as analytics pings, non-GET requests.</p>
  661. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'fetch'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  662. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">));</span>
  663. <span class="c1">// or simply don't call event.respondWith, which</span>
  664. <span class="c1">// will result in default browser behaviour</span>
  665. <span class="p">});</span>
  666. </pre></div>
  667. <p>…although you don't often need to handle this case specifically, <a href="#cache-falling-back-to-network">"Cache, falling back to network"</a> covers it.</p>
  668. <h3 id="cache-falling-back-to-network">Cache, falling back to network</h3>
  669. <div class="diagram-container"><svg viewbox="0 0 549.92853 265"><g transform="translate(-36.352 -120.593)"><text x="248.603" y="384.105" class="label">ServiceWorker</text><use xlink:href="#diagram-sw" transform="translate(160.325 204.743)"/><text x="89.548" y="252.972" class="label">Page</text><use transform="translate(-31.309 48.726)" xlink:href="#diagram-page"/><text x="537.439" y="383.491" class="label">Cache</text><use xlink:href="#diagram-cache" transform="translate(461.439 210.129)"/><text x="400.625" y="253.272" class="label">Network</text><use transform="translate(317.347 80.91)" xlink:href="#diagram-network"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M125.228 217.424c-.572 41.786 20.007 74.986 60.648 75.56"/><path d="M177.64 287.113l10.897 5.65-10.857 5.002"/></g><text x="189.791" y="151.852" class="fade-in state-label" transform="translate(-33.772 202.062) translate(0 -90)">1</text></g><g class="anim"><g class="joiner"><path d="M305.727 320.923c63.06 22.954 117.92 22.76 182.588 6.315"/><path d="M477.033 324.593l15.393 1.14-11.972 9.692"/></g><text x="215.445" y="131.329" class="fade-in state-label" transform="translate(-33.772 202.062) translate(219.43 -3.206)">2</text></g><g class="anim"><g class="cross"><path d="M487.304 315.19l18.813 19.955"/><path d="M503.267 312.91l-14.252 23.375"/></g></g><g class="anim"><g class="joiner"><path d="M306.297 294.13c41.367 4.712 30.642-61.09 53.737-84.936"/><path d="M350.672 212.27l10.33-5.536-.98 12.597"/></g><text class="fade-in state-label" y="93.136" x="140.753" transform="translate(-33.772 202.062) translate(220 -30)">3</text></g><g class="anim"><g class="joiner"><path d="M349.054 172.7c-48.11-26.075-154.585-24.56-198.804-3.377"/><path d="M156.36 160.117l-9.12 9.69 11.97 1.712"/></g><text x="145.324" y="91.993" class="fade-in state-label" transform="translate(-33.772 202.062) translate(129.355 -98.981)">4</text></g></g></g></svg></div>
  670. <p><strong>Ideal for:</strong> If you're building offline-first, this is how you'll handle the majority of requests. Other patterns will be exceptions based on the incoming request.</p>
  671. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'fetch'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  672. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span>
  673. <span class="nx">caches</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  674. <span class="k">return</span> <span class="nx">response</span> <span class="o">||</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">);</span>
  675. <span class="p">})</span>
  676. <span class="p">);</span>
  677. <span class="p">});</span>
  678. </pre></div>
  679. <p>This gives you the "Cache only" behaviour for things in the cache and the "Network only" behaviour for anything not-cached (which includes all non-GET requests, as they cannot be cached).</p>
  680. <h3 id="cache-network-race">Cache &amp; network race</h3>
  681. <div class="diagram-container"><svg viewbox="0 0 549.92853 293"><g transform="translate(-36.352 -92.593)"><text x="317.603" y="306.854" class="label">ServiceWorker</text><use xlink:href="#diagram-sw" transform="translate(229.325 127.491)"/><text x="86.548" y="295.228" class="label">Page</text><use transform="translate(-34.309 98.982)" xlink:href="#diagram-page"/><text x="537.439" y="383.491" class="label">Network</text><use xlink:href="#diagram-network" transform="translate(455.439 210.129)"/><text x="535.625" y="226.272" class="label">Cache</text><use transform="translate(458.347 53.91)" xlink:href="#diagram-cache"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M144.082 222.997l108.22.477"/><path d="M244.636 217.885l10.898 5.65-10.857 5.002"/></g><text x="213.863" y="96.258" class="fade-in state-label" transform="translate(-33.772 202.062) translate(20 -85)">1</text></g><g class="anim"><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M376.777 239.045s118.118-2.7 119.473 23.924"/><path d="M489.94 259.152l7.46 3.93-.54-10.473"/></g><text x="213.29" y="137.231" class="fade-in state-label" transform="translate(-33.772 202.062) translate(252.695 -68.952)">2</text></g><g class="anim"><g class="joiner"><path d="M489.84 323.937c-38.882 22.15-81.557 44.644-127.76 40.887-38.15-3.204-67.514-36.343-106.685-34.695-32.35 4.344-58.522 31.056-92.25 28.738-17.27 1.488-28.24-27.232-9.825-33.672 12.157-5.75 32.496-8.91 32.273-26.037"/></g><text class="fade-in state-label" y="140.051" x="464.52" transform="translate(-33.772 202.062)">3</text></g><g class="cross"><path d="M179.025 290.99l13.05 18.8"/><path d="M194.412 293.32l-18.862 13.577"/></g></g><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M376.204 206.376s112.388 4.54 115.096-15.57"/><path d="M483.53 193.605l9.857-2.738-4.656 10.732"/></g><text x="213.863" y="96.258" class="fade-in state-label" transform="translate(-33.772 202.062) translate(252.122 -101.621)">2</text></g><g class="anim"><g class="joiner"><path d="M489.74 126.782c-277.096-7.502-405.27-8.293-402.175 48.8"/><path d="M82.923 166.3l3.953 10.395 6.666-10.614"/></g><text x="465.672" y="-48.95" class="fade-in state-label" transform="translate(-33.772 202.062)">3</text></g></g></g></g></g></svg></div>
  682. <p><strong>Ideal for:</strong> Small assets where you're chasing performance on devices with slow disk access.</p>
  683. <p>With some combinations of older hard drives, virus scanners, and faster internet connections, getting resources from the network can be quicker than going to disk. However, going to the network when the user has the content on their device can be a waste of data, so bear that in mind.</p>
  684. <div class="codehilite"><pre><span class="c1">// Promise.race is no good to us because it rejects if</span>
  685. <span class="c1">// a promise rejects before fulfilling. Let's make a proper</span>
  686. <span class="c1">// race function:</span>
  687. <span class="kd">function</span> <span class="nx">properRace</span><span class="p">(</span><span class="nx">promises</span><span class="p">)</span> <span class="p">{</span>
  688. <span class="c1">// we implement by inverting Promise.all</span>
  689. <span class="nx">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span>
  690. <span class="nx">promises</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">promise</span><span class="p">)</span> <span class="p">{</span>
  691. <span class="c1">// for each promise, cast it, then swap around rejection &amp; fulfill</span>
  692. <span class="k">return</span> <span class="nx">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">promise</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
  693. <span class="k">throw</span> <span class="nx">val</span><span class="p">;</span>
  694. <span class="p">},</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
  695. <span class="k">return</span> <span class="nx">err</span><span class="p">;</span>
  696. <span class="p">});</span>
  697. <span class="p">})</span>
  698. <span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">errs</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// then swap it back</span>
  699. <span class="k">throw</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">"Proper race: none fulfilled"</span><span class="p">);</span>
  700. <span class="p">},</span> <span class="kd">function</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
  701. <span class="k">return</span> <span class="nx">val</span><span class="p">;</span>
  702. <span class="p">});</span>
  703. <span class="p">}</span>
  704. <span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'fetch'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  705. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span>
  706. <span class="nx">properRace</span><span class="p">([</span>
  707. <span class="nx">caches</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">),</span>
  708. <span class="nx">fetch</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">)</span>
  709. <span class="p">])</span>
  710. <span class="p">);</span>
  711. <span class="p">});</span>
  712. </pre></div>
  713. <h3 id="network-falling-back-to-cache">Network falling back to cache</h3>
  714. <div class="diagram-container"><svg viewbox="0 0 549.92853 265"><g transform="translate(-36.352 -120.593)"><text x="248.603" y="384.105" class="label">ServiceWorker</text><use xlink:href="#diagram-sw" transform="translate(160.325 204.743)"/><text x="89.548" y="252.972" class="label">Page</text><use transform="translate(-31.309 48.726)" xlink:href="#diagram-page"/><text x="537.439" y="383.491" class="label">Network</text><use xlink:href="#diagram-network" transform="translate(455.439 210.129)"/><text x="400.625" y="253.272" class="label">Cache</text><use transform="translate(323.347 80.91)" xlink:href="#diagram-cache"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M125.228 217.424c-.572 41.786 20.007 74.986 60.648 75.56"/><path d="M177.64 287.113l10.897 5.65-10.857 5.002"/></g><text x="189.791" y="151.852" class="fade-in state-label" transform="translate(-33.772 202.062) translate(0 -90)">1</text></g><g class="anim"><g class="joiner"><path d="M305.727 320.923c63.06 22.954 117.92 22.76 182.588 6.315"/><path d="M477.033 324.593l15.393 1.14-11.972 9.692"/></g><text x="215.445" y="131.329" class="fade-in state-label" transform="translate(-33.772 202.062) translate(219.43 -3.206)">2</text></g><g class="anim"><g class="cross"><path d="M487.304 315.19l18.813 19.955"/><path d="M503.267 312.91l-14.252 23.375"/></g></g><g class="anim"><g class="joiner"><path d="M306.297 294.13c53.367 3.57 53.5-42.803 58.308-70.08"/><path d="M358.1 231.7l6.902-10.108 3.593 12.025"/></g><text class="fade-in state-label" y="91.993" x="145.324" transform="translate(-33.772 202.062) translate(220 -30)">3</text></g><g class="anim"><g class="joiner"><path d="M349.054 172.7c-48.11-26.075-154.585-24.56-198.804-3.377"/><path d="M156.36 160.117l-9.12 9.69 11.97 1.712"/></g><text x="145.324" y="91.993" class="fade-in state-label" transform="translate(-33.772 202.062) translate(129.355 -98.981)">4</text></g></g></g></svg></div>
  715. <p><strong>Ideal for:</strong> A quick-fix for resources that update frequently, outside of the "version" of the site. E.g. articles, avatars, social media timelines, game leader boards.</p>
  716. <p>This means you give online users the most up-to-date content, but offline users get an older cached version. If the network request succeeds you'll most-likely want to <a href="#on-network-response">update the cache entry</a>.</p>
  717. <p>However, this method has flaws. If the user has an intermittent or slow connection they'll have to wait for the network to fail before they get the perfectly acceptable content already on their device. This can take an extremely long time and is a frustrating user experience. See the next pattern, <a href="#cache-then-network">"Cache then network"</a>, for a better solution.</p>
  718. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'fetch'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  719. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span>
  720. <span class="nx">fetch</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">).</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  721. <span class="k">return</span> <span class="nx">caches</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">);</span>
  722. <span class="p">})</span>
  723. <span class="p">);</span>
  724. <span class="p">});</span>
  725. </pre></div>
  726. <h3 id="cache-then-network">Cache then network</h3>
  727. <div class="diagram-container"><svg viewbox="0 0 549.92853 330"><g transform="translate(-36.352 -55.593)"><text x="315.298" y="193.914" class="label">ServiceWorker</text><use xlink:href="#diagram-sw" transform="translate(227.021 14.552)"/><text x="86.548" y="295.228" class="label">Page</text><use transform="translate(-34.309 98.982)" xlink:href="#diagram-page"/><text x="538.015" y="303.972" class="label">Network</text><use xlink:href="#diagram-network" transform="translate(456.016 130.61)"/><text x="318.965" y="383.005" class="label">Cache</text><use transform="translate(241.688 210.642)" xlink:href="#diagram-cache"/><g class="anim"><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M124.49 265.638c3.457 67.418 71.916 61.556 141.063 63.86"/><path d="M257.888 323.91l10.898 5.65-10.857 5.002"/></g><text class="fade-in state-label" y="353.829" x="270.333" transform="translate(107.978 248.16) translate(-205.303 -248.071)">1</text></g><g class="anim"><g class="joiner"><path d="M269.12 288.11c-36.3 0-69.832-45.044-121.692-46.196"/><path d="M154.744 236.325l-10.998 5.65 11.04 6.153"/></g><text x="285.891" y="282.954" class="fade-in state-label" transform="translate(107.978 248.16) translate(-205.303 -248.071)">2</text></g></g><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M125.315 179.113c1.728-51.86 61.655-71.452 131.378-72.604"/><path d="M247.25 101.515l10.37 4.61-10.947 6.34"/></g><text class="fade-in state-label" y="-125.012" x="36.387" transform="translate(107.978 248.16)">1</text></g><g class="anim"><g class="joiner"><path d="M370.21 108.814c66.264-1.153 174.018 1.152 169.408 53.588"/><path d="M544.58 152.222l-4.61 13.253-6.915-13.253"/></g><text x="59.436" y="-137.112" class="fade-in state-label" transform="translate(107.978 248.16) translate(274.858 -8.643)">2</text></g><g class="anim"><g class="joiner"><path d="M484.877 216.567l-339.395-1.152"/><path d="M153.324 220.793l-10.372-5.186 9.796-6.34"/></g><text class="fade-in state-label" y="-34.24" x="53.097" transform="translate(107.978 248.16) translate(274.858 -8.643)">3</text><g class="joiner"><path d="M484.877 216.568l-80.894-.275c-20.265-.068-50.397 37.642-50.397 37.642"/><path d="M355.598 243.91l-2.325 12.032 8.82-5.693"/></g></g></g></g></g></svg></div>
  728. <p><strong>Ideal for:</strong> Content that updates frequently. E.g. articles, social media timelines, game leaderboards.</p>
  729. <p>This requires the page to make two requests, one to the cache, one to the network. The idea is to show the cached data first, then update the page when/if the network data arrives.</p>
  730. <p>Sometimes you can just replace the current data when new data arrives (e.g. game leaderboard), but that can be disruptive with larger pieces of content. Basically, don't "disappear" something the user may be reading or interacting with.</p>
  731. <p>Twitter adds the new content above the old content &amp; adjusts the scroll position so the user is uninterrupted. This is possible because Twitter mostly retains a mostly-linear order to content. I copied this pattern for <a href="https://jakearchibald.github.io/trained-to-thrill/">trained-to-thrill</a> to get content on screen as fast as possible, but still display up-to-date content once it arrives.</p>
  732. <h4 id="code-in-the-page">Code in the page:</h4>
  733. <div class="codehilite"><pre><span class="kd">var</span> <span class="nx">networkDataReceived</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
  734. <span class="nx">startSpinner</span><span class="p">();</span>
  735. <span class="c1">// fetch fresh data</span>
  736. <span class="kd">var</span> <span class="nx">networkUpdate</span> <span class="o">=</span> <span class="nx">fetch</span><span class="p">(</span><span class="s1">'/data.json'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  737. <span class="k">return</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
  738. <span class="p">}).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
  739. <span class="nx">networkDataReceived</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
  740. <span class="nx">updatePage</span><span class="p">();</span>
  741. <span class="p">});</span>
  742. <span class="c1">// fetch cached data</span>
  743. <span class="nx">caches</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="s1">'/data.json'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  744. <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">)</span> <span class="k">throw</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">"No data"</span><span class="p">);</span>
  745. <span class="k">return</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
  746. <span class="p">}).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
  747. <span class="c1">// don't overwrite newer network data</span>
  748. <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">networkDataReceived</span><span class="p">)</span> <span class="p">{</span>
  749. <span class="nx">updatePage</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
  750. <span class="p">}</span>
  751. <span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  752. <span class="c1">// we didn't get cached data, the network is our last hope:</span>
  753. <span class="k">return</span> <span class="nx">networkUpdate</span><span class="p">;</span>
  754. <span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="nx">showErrorMessage</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">stopSpinner</span><span class="p">);</span>
  755. </pre></div>
  756. <h4 id="code-in-the-serviceworker">Code in the ServiceWorker:</h4>
  757. <p>We always go to the network &amp; update a cache as we go.</p>
  758. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'fetch'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  759. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span>
  760. <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'mysite-dynamic'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">cache</span><span class="p">)</span> <span class="p">{</span>
  761. <span class="k">return</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">clone</span><span class="p">()).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  762. <span class="nx">cache</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">,</span> <span class="nx">response</span><span class="p">.</span><span class="nx">clone</span><span class="p">());</span>
  763. <span class="k">return</span> <span class="nx">response</span><span class="p">;</span>
  764. <span class="p">});</span>
  765. <span class="p">})</span>
  766. <span class="p">);</span>
  767. <span class="p">});</span>
  768. </pre></div>
  769. <p>The above doesn't work in Chrome yet, as we've yet to expose <code>fetch</code> and <code>caches</code> to pages (<a href="https://code.google.com/p/chromium/issues/detail?id=436770">ticket #1</a>, <a href="https://code.google.com/p/chromium/issues/detail?id=439389">ticket #2</a>).</p>
  770. <p>In <a href="https://jakearchibald.github.io/trained-to-thrill/">trained-to-thrill</a> I worked around this by using <a href="https://github.com/jakearchibald/trained-to-thrill/blob/3291dd40923346e3cc9c83ae527004d502e0464f/www/static/js-unmin/utils.js#L3">XHR instead of fetch</a>, and abusing the <code>Accept</code> header to tell the ServiceWorker where to get the result from (<a href="https://github.com/jakearchibald/trained-to-thrill/blob/3291dd40923346e3cc9c83ae527004d502e0464f/www/static/js-unmin/index.js#L70">page code</a>, <a href="https://github.com/jakearchibald/trained-to-thrill/blob/3291dd40923346e3cc9c83ae527004d502e0464f/www/static/js-unmin/sw/index.js#L61">ServiceWorker code</a>).</p>
  771. <h3 id="generic-fallback">Generic fallback</h3>
  772. <div class="diagram-container"><svg viewbox="0 0 549.92853 265"><g transform="translate(-36.352 -120.593)"><text x="248.603" y="384.105" class="label">ServiceWorker</text><use xlink:href="#diagram-sw" transform="translate(160.325 204.743)"/><text x="89.548" y="252.972" class="label">Page</text><use transform="translate(-31.309 48.726)" xlink:href="#diagram-page"/><text x="537.439" y="383.491" class="label">Network</text><use xlink:href="#diagram-network" transform="translate(455.439 210.129)"/><text x="400.625" y="253.272" class="label">Cache</text><use transform="translate(323.347 80.91)" xlink:href="#diagram-cache"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M125.228 217.424c-.572 41.786 20.007 74.986 60.648 75.56"/><path d="M177.64 287.113l10.897 5.65-10.857 5.002"/></g><text x="189.791" y="151.852" class="fade-in state-label" transform="translate(-33.772 202.062) translate(0 -90)">1</text></g><g class="anim"><g class="joiner"><path d="M306.297 294.13c53.367 3.57 53.5-42.803 58.308-70.08"/><path d="M358.1 231.7l6.902-10.108 3.593 12.025"/></g><text class="fade-in state-label" y="116.993" x="180.962" transform="translate(-33.772 202.062) translate(220 -30)">2</text></g><g class="anim"><g class="cross"><path d="M355.304 209.19l18.813 19.955"/><path d="M371.267 206.91l-14.252 23.375"/></g></g><g class="anim"><g class="joiner"><path d="M305.727 320.923c63.06 22.954 117.92 22.76 182.588 6.315"/><path d="M477.033 324.593l15.393 1.14-11.972 9.692"/></g><text x="214.381" y="167.499" class="fade-in state-label" transform="translate(-33.772 202.062) translate(219.43 -3.206)">3</text></g><g class="anim"><g class="cross"><path d="M487.304 315.19l18.813 19.955"/><path d="M503.267 312.91l-14.252 23.375"/></g></g><g class="anim"><g class="joiner"><path d="M250.117 242.337c-1.063-37.234 66.623-45.397 99.6-54.972"/><path d="M336.148 185.074l14.283 2.245-9.836 8.624"/></g><text class="fade-in state-label" y="133.95" x="185.218" transform="translate(-33.772 202.062) translate(129.355 -98.981)">4</text></g><g class="anim"><g class="joiner"><path d="M349.054 156.7c-48.11-26.075-154.585-24.56-198.804-3.377"/><path d="M156.36 144.117l-9.12 9.69 11.97 1.712"/></g><text x="145.324" y="67.993" class="fade-in state-label" transform="translate(-33.772 202.062) translate(129.355 -98.981)">5</text></g></g></g></svg></div>
  773. <p>If you fail to serve something from the cache and/or network you may want to provide a generic fallback.</p>
  774. <p><strong>Ideal for:</strong> Secondary imagery such as avatars, failed POST requests, "Unavailable while offline" page.</p>
  775. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'fetch'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  776. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span>
  777. <span class="c1">// Try the cache</span>
  778. <span class="nx">caches</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  779. <span class="c1">// Fall back to network</span>
  780. <span class="k">return</span> <span class="nx">response</span> <span class="o">||</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">);</span>
  781. <span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  782. <span class="c1">// If both fail, show a generic fallback:</span>
  783. <span class="k">return</span> <span class="nx">caches</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="s1">'/offline.html'</span><span class="p">);</span>
  784. <span class="c1">// However, in reality you'd have many different</span>
  785. <span class="c1">// fallbacks, depending on URL &amp; headers.</span>
  786. <span class="c1">// Eg, a fallback silhouette image for avatars.</span>
  787. <span class="p">})</span>
  788. <span class="p">);</span>
  789. <span class="p">});</span>
  790. </pre></div>
  791. <p>The item you fallback to is likely to be an <a href="#on-install-as-a-dependency">install dependency</a>.</p>
  792. <p>If your page is posting an email, your ServiceWorker may fall back to storing the email in an IDB 'outbox' &amp; respond letting the page know that the send failed but the data was successfully retained.</p>
  793. <h3 id="serviceworker-side-templating">ServiceWorker-side templating</h3>
  794. <div class="diagram-container"><svg viewbox="0 0 549.92853 316"><g transform="translate(-36.352 -69.593)"><text x="315.298" y="209.914" class="label">ServiceWorker</text><use xlink:href="#diagram-sw" transform="translate(227.021 30.552)"/><text x="86.548" y="311.228" class="label">Page</text><use transform="translate(-34.309 114.982)" xlink:href="#diagram-page"/><text x="540.296" y="321.492" class="label">Cache</text><use xlink:href="#diagram-cache" transform="translate(463.617 148.13)"/><text x="314.093" y="368.6" class="label">Templating Engine</text><use transform="translate(231.806 194.717)" xlink:href="#diagram-template"/><g class="anim sequence"><g class="anim"><g class="joiner"><path d="M89.255 192.108c1.728-52.86 92.707-70.45 167.438-69.6"/><path d="M247.248 117.515l10.372 4.61-10.948 6.34"/></g><text class="fade-in state-label" y="-136.03" x="28.373" transform="translate(107.978 248.16) translate(0 16)">1</text></g><g class="anim"><g class="joiner"><path d="M369.285 114.266c107.177 1.52 167.987 38.007 171.028 67.65l3.8-7.6"/></g><text x="381.506" y="-138.692" class="fade-in state-label" transform="translate(107.978 248.16) translate(0 16)">2</text><g class="joiner"><path d="M369.285 120.267c103.207 1.52 162.528 38.006 165.072 63.68l-6.125-5.616"/></g></g><g class="anim"><g class="joiner"><path d="M491.95 231.364c-49.134.993-68.986 41.193-123.578 41.193l6.948-5.46"/></g><g class="joiner"><path d="M491.95 237.364c-45.164.496-64.52 41.69-123.578 41.193l7.94 5.46"/></g><text class="fade-in state-label" y="24.589" x="336.344" transform="translate(107.978 248.16) translate(0 16)">3</text></g><g class="anim"><g class="joiner"><path d="M263.284 276.01c-50.88.25-69.862-37.208-119.25-39.353"/><path d="M153.945 231.167l-10.97 6.098 11.386 6.34"/></g><text x="91.478" y="40.235" class="fade-in state-label" transform="translate(107.978 248.16)">4</text></g></g></g></svg></div>
  795. <p><strong>Ideal for:</strong> Pages that cannot have their server response cached.</p>
  796. <p><a href="http://jakearchibald.com/2013/progressive-enhancement-is-faster/">Rendering pages on the server makes things fast</a>, but that can mean including state data that may not make sense in a cache, e.g. "Logged in as…". If your page is controlled by a ServiceWorker, you may instead choose to request JSON data along with a template, and render that instead.</p>
  797. <div class="codehilite"><pre><span class="nx">importScripts</span><span class="p">(</span><span class="s1">'templating-engine.js'</span><span class="p">);</span>
  798. <span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'fetch'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  799. <span class="kd">var</span> <span class="nx">requestURL</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">URL</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">);</span>
  800. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span>
  801. <span class="nx">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">([</span>
  802. <span class="nx">caches</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="s1">'/article-template.html'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  803. <span class="k">return</span> <span class="nx">response</span><span class="p">.</span><span class="nx">text</span><span class="p">();</span>
  804. <span class="p">}),</span>
  805. <span class="nx">caches</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">requestURL</span><span class="p">.</span><span class="nx">path</span> <span class="o">+</span> <span class="s1">'.json'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  806. <span class="k">return</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
  807. <span class="p">})</span>
  808. <span class="p">]).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">responses</span><span class="p">)</span> <span class="p">{</span>
  809. <span class="kd">var</span> <span class="nx">template</span> <span class="o">=</span> <span class="nx">responses</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
  810. <span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">responses</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
  811. <span class="k">return</span> <span class="k">new</span> <span class="nx">Response</span><span class="p">(</span><span class="nx">renderTemplate</span><span class="p">(</span><span class="nx">template</span><span class="p">,</span> <span class="nx">data</span><span class="p">),</span> <span class="p">{</span>
  812. <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
  813. <span class="s1">'Content-Type'</span><span class="o">:</span> <span class="s1">'text/html'</span>
  814. <span class="p">}</span>
  815. <span class="p">});</span>
  816. <span class="p">})</span>
  817. <span class="p">);</span>
  818. <span class="p">});</span>
  819. </pre></div>
  820. <h2 id="putting-it-together">Putting it together</h2>
  821. <p>You don't have to pick one of these methods, you'll likely use many of them depending on request URL. For example, <a href="https://jakearchibald.github.io/trained-to-thrill/">trained-to-thrill</a> uses:</p>
  822. <p>Just look at the request and decide what to do:</p>
  823. <div class="codehilite"><pre><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'fetch'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  824. <span class="c1">// Parse the URL:</span>
  825. <span class="kd">var</span> <span class="nx">requestURL</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">URL</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
  826. <span class="c1">// Handle requests to a particular host specifically</span>
  827. <span class="k">if</span> <span class="p">(</span><span class="nx">requestURL</span><span class="p">.</span><span class="nx">hostname</span> <span class="o">==</span> <span class="s1">'api.example.com'</span><span class="p">)</span> <span class="p">{</span>
  828. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span><span class="cm">/* some combination of patterns */</span><span class="p">);</span>
  829. <span class="k">return</span><span class="p">;</span>
  830. <span class="p">}</span>
  831. <span class="c1">// Routing for local URLs</span>
  832. <span class="k">if</span> <span class="p">(</span><span class="nx">requestURL</span><span class="p">.</span><span class="nx">origin</span> <span class="o">==</span> <span class="nx">location</span><span class="p">.</span><span class="nx">origin</span><span class="p">)</span> <span class="p">{</span>
  833. <span class="c1">// Handle article URLs</span>
  834. <span class="k">if</span> <span class="p">(</span><span class="sr">/^\/article\//</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">requestURL</span><span class="p">.</span><span class="nx">pathname</span><span class="p">))</span> <span class="p">{</span>
  835. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span><span class="cm">/* some other combination of patterns */</span><span class="p">);</span>
  836. <span class="k">return</span><span class="p">;</span>
  837. <span class="p">}</span>
  838. <span class="k">if</span> <span class="p">(</span><span class="sr">/\.webp$/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">requestURL</span><span class="p">.</span><span class="nx">pathname</span><span class="p">))</span> <span class="p">{</span>
  839. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span><span class="cm">/* some other combination of patterns */</span><span class="p">);</span>
  840. <span class="k">return</span><span class="p">;</span>
  841. <span class="p">}</span>
  842. <span class="k">if</span> <span class="p">(</span><span class="nx">request</span><span class="p">.</span><span class="nx">method</span> <span class="o">==</span> <span class="s1">'POST'</span><span class="p">)</span> <span class="p">{</span>
  843. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span><span class="cm">/* some other combination of patterns */</span><span class="p">);</span>
  844. <span class="k">return</span><span class="p">;</span>
  845. <span class="p">}</span>
  846. <span class="k">if</span> <span class="p">(</span><span class="sr">/cheese/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">requestURL</span><span class="p">.</span><span class="nx">pathname</span><span class="p">))</span> <span class="p">{</span>
  847. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span>
  848. <span class="k">new</span> <span class="nx">Response</span><span class="p">(</span><span class="s2">"Flagrant cheese error"</span><span class="p">,</span> <span class="p">{</span>
  849. <span class="nx">status</span><span class="o">:</span> <span class="mi">512</span>
  850. <span class="p">})</span>
  851. <span class="p">);</span>
  852. <span class="k">return</span><span class="p">;</span>
  853. <span class="p">}</span>
  854. <span class="p">}</span>
  855. <span class="c1">// A sensible default pattern</span>
  856. <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span>
  857. <span class="nx">caches</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  858. <span class="k">return</span> <span class="nx">response</span> <span class="o">||</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">);</span>
  859. <span class="p">})</span>
  860. <span class="p">);</span>
  861. <span class="p">});</span>
  862. </pre></div>
  863. <p>…you get the picture.</p>
  864. <p>If you come up with additional patterns, throw them at me in the comments!</p>
  865. </article>
  866. </section>
  867. <nav id="jumpto">
  868. <p>
  869. <a href="/david/blog/">Accueil du blog</a> |
  870. <a href="http://jakearchibald.com/2014/offline-cookbook/">Source originale</a> |
  871. <a href="/david/stream/2019/">Accueil du flux</a>
  872. </p>
  873. </nav>
  874. <footer>
  875. <div>
  876. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  877. <p>
  878. Bonjour/Hi!
  879. Je suis <a href="/david/" title="Profil public">David&nbsp;Larlet</a>, je vis actuellement à Montréal et j’alimente cet espace depuis 15 ans. <br>
  880. Si tu as apprécié cette lecture, n’hésite pas à poursuivre ton exploration. Par exemple via les <a href="/david/blog/" title="Expériences bienveillantes">réflexions bimestrielles</a>, la <a href="/david/stream/2019/" title="Pensées (dés)articulées">veille hebdomadaire</a> ou en t’abonnant au <a href="/david/log/" title="S’abonner aux publications via RSS">flux RSS</a> (<a href="/david/blog/2019/flux-rss/" title="Tiens c’est quoi un flux RSS ?">so 2005</a>).
  881. </p>
  882. <p>
  883. Je m’intéresse à la place que je peux avoir dans ce monde. En tant qu’humain, en tant que membre d’une famille et en tant qu’associé d’une coopérative. De temps en temps, je fais aussi des <a href="https://github.com/davidbgk" title="Principalement sur Github mais aussi ailleurs">trucs techniques</a>. Et encore plus rarement, <a href="/david/talks/" title="En ce moment je laisse plutôt la place aux autres">j’en parle</a>.
  884. </p>
  885. <p>
  886. Voici quelques articles choisis :
  887. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  888. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  889. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  890. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  891. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  892. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  893. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  894. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  895. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  896. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  897. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  898. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  899. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  900. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  901. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  902. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  903. </p>
  904. <p>
  905. On peut <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">échanger par courriel</a>. Si éventuellement tu souhaites que l’on travaille ensemble, tu devrais commencer par consulter le <a href="http://larlet.com">profil dédié à mon activité professionnelle</a> et/ou contacter directement <a href="http://scopyleft.fr/">scopyleft</a>, la <abbr title="Société coopérative et participative">SCOP</abbr> dont je fais partie depuis six ans. Je recommande au préalable de lire <a href="/david/blog/2018/cout-site/" title="Attention ce qui va suivre peut vous choquer">combien coûte un site</a> et pourquoi je suis plutôt favorable à une <a href="/david/pro/devis/" title="Discutons-en !">non-demande de devis</a>.
  906. </p>
  907. <p>
  908. Je ne traque pas ta navigation mais mon
  909. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  910. conserve des logs d’accès.
  911. </p>
  912. </div>
  913. </footer>
  914. <script type="text/javascript">
  915. ;(_ => {
  916. const jumper = document.getElementById('jumper')
  917. jumper.addEventListener('click', e => {
  918. e.preventDefault()
  919. const anchor = e.target.getAttribute('href')
  920. const targetEl = document.getElementById(anchor.substring(1))
  921. targetEl.scrollIntoView({behavior: 'smooth'})
  922. })
  923. })()
  924. </script>