<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Jeffrey Posnick on Medium]]></title>
        <description><![CDATA[Stories by Jeffrey Posnick on Medium]]></description>
        <link>https://medium.com/@jeffposnick?source=rss-3fb739097395------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*G2CDeH7mjECd9Daw.jpg</url>
            <title>Stories by Jeffrey Posnick on Medium</title>
            <link>https://medium.com/@jeffposnick?source=rss-3fb739097395------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 05 Nov 2026 12:57:47 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@jeffposnick/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[CDS Perf Review Clinic Takeaways]]></title>
            <link>https://medium.com/@jeffposnick/cds-perf-review-clinic-takeaways-cb0eaed87b06?source=rss-3fb739097395------2</link>
            <guid isPermaLink="false">https://medium.com/p/cb0eaed87b06</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[performance]]></category>
            <dc:creator><![CDATA[Jeffrey Posnick]]></dc:creator>
            <pubDate>Fri, 10 Nov 2017 12:00:00 GMT</pubDate>
            <atom:updated>2017-11-10T20:29:23.037Z</atom:updated>
            <content:encoded><![CDATA[<h3>CDS ’17 Retrospective</h3><p><a href="https://developer.chrome.com/devsummit/">Chrome Dev Summit 2017</a> is behind us. I spent most of the run-up to the event putting together my <a href="https://www.youtube.com/watch?v=DtuJ55tmjps">“Workbox: Flexible PWA Libraries” talk</a>, but beyond that, I was also involved in <a href="https://twitter.com/jeffposnick/status/922899094053330944">running on-site Performance Review Clinics</a> that attendees could sign up for.</p><p>It’s always a privilege to meet with the developers who are building web apps that I use every day, and to lend whatever guidance I could offer. Looking into the performance of a large group of production websites gives insights that you wouldn’t get from just examining a site as a one-off. Patterns start to emerge about common areas for improvement.</p><p>I wanted to publicly share a few high-level groupings based on those patterns, and identify a handful of links for each area that contain useful guidance. It’s by no means exhaustive, but I can say that each of these links contains solutions to a real-world performance issue that we identified during the Performance Review Clinics.</p><h3>Linkdump!</h3><p>Huge thanks go out to all the authors, many of whom I’m lucky enough to work with.</p><h4>Overall Migration/End to End Case Study</h4><ul><li><a href="https://medium.com/dev-channel/treebo-a-react-and-preact-progressive-web-app-performance-case-study-5e4f450d5299">A React And Preact Progressive Web App Performance Case Study: Treebo</a></li></ul><h4>Loading and Prioritization</h4><ul><li><a href="https://developers.google.com/web/fundamentals/performance/http2/">Introduction to HTTP/2</a></li><li><a href="https://developers.google.com/web/fundamentals/performance/resource-prioritization">Resource Prioritization — Getting the Browser to Help You</a></li><li><a href="https://www.html5rocks.com/en/tutorials/speed/script-loading/">Deep dive into the murky waters of script loading</a></li></ul><h4>JavaScript</h4><ul><li><a href="https://infrequently.org/2017/10/can-you-afford-it-real-world-web-performance-budgets/">Can You Afford It?: Real-world Web Performance Budgets</a></li><li><a href="https://medium.com/reloading/javascript-start-up-performance-69200f43b201">JavaScript Start-up Performance</a></li></ul><h4>CSS</h4><ul><li><a href="https://github.com/addyosmani/critical">critical: Extract &amp; Inline Critical-path CSS in HTML pages</a></li><li><a href="https://jakearchibald.com/2016/link-in-body/">The future of loading CSS</a></li><li><a href="https://github.com/filamentgroup/loadCSS/">loadCss: A function for loading CSS asynchronously</a></li></ul><h4>Caching</h4><ul><li><a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching">HTTP Caching</a></li><li><a href="https://jakearchibald.com/2016/caching-best-practices/">Caching best practices &amp; max-age gotchas</a></li><li><a href="https://developers.google.com/web/fundamentals/primers/service-workers/">Service Workers: an Introduction</a></li><li><a href="https://developers.google.com/web/tools/workbox/">Workbox: JavaScript libraries for Progressive Web Apps</a></li></ul><h4>Images/Multimedia Content</h4><ul><li><a href="https://images.guide/">Essential Image Optimization</a></li><li><a href="https://deanhume.com/home/blogpost/lazy-loading-images-using-intersection-observer/10163">Lazy Loading Images Using Intersection Observer</a></li><li><a href="https://css-tricks.com/icon-fonts-vs-svg/">Inline SVG vs Icon Fonts [CAGEMATCH]</a></li><li><a href="https://css-tricks.com/content-jumping-avoid/">Content Jumping (and How To Avoid It)</a></li><li><a href="https://www.labnol.org/internet/light-youtube-embeds/27941/">A Better Method for Embedding YouTube Videos on your Website</a></li></ul><p><em>Originally published at </em><a href="https://jeffy.info/2017/11/10/post-cds-perf-links.html"><em>jeffy.info</em></a><em> on November 10, 2017.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cb0eaed87b06" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[auxclick is Coming to Chrome 55]]></title>
            <link>https://medium.com/dev-channel/auxclick-is-coming-to-chrome-55-c8b0ad5a922?source=rss-3fb739097395------2</link>
            <guid isPermaLink="false">https://medium.com/p/c8b0ad5a922</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Jeffrey Posnick]]></dc:creator>
            <pubDate>Thu, 20 Oct 2016 17:09:51 GMT</pubDate>
            <atom:updated>2016-10-20T17:25:00.808Z</atom:updated>
            <content:encoded><![CDATA[<p>When is a click not a click? For a web developer working on a complex user interface, that&#39;s not an abstract philosophical question. If you&#39;re implementing custom mouse input behavior, it&#39;s crucial to keep user intent in mind. If a user clicks on a link with their mouse&#39;s middle button, for instance, then it&#39;s reasonable to assume that they wanted to open a new tab with that link&#39;s contents. If a user middle-clicks on a random UI element, then it you may want to assume that it was inadvertent, and ignore that input, while a primary button click would be expected to trigger a response from the UI.</p><p>It’s possible, if a bit cumbersome, to model these nuanced interactions via a single click event listener. You would have to explicitly check the <a href="https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button">button</a> property of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent">MouseEvent</a>, to see whether it was set to 0, representing the primary button, versus anything else, with 1 usually representing the middle button, and so on. But not many developers go as far as explicitly checking the button property, leading to code that handles all clicks identically, regardless of which button was pressed.</p><p>Starting with Chrome 55, a new type of MouseEvent, called auxclick, is fired in response to any clicks made with a non-primary button. Accompanying this new event is a corresponding change in behavior of the click event: it will only fire when the primary mouse button is pressed. We hope that these changes will make it easier for web developers to write event handlers that respond to only the type of clicks that they care about, without having to specifically check the MouseEvent.button property.</p><h3>Reduce false positives</h3><p>As mentioned, one motivation for creating auxclick was to avoid deployment of custom click handlers that mistakenly override the &quot;middle-click-opens-a-tab&quot; behavior. For example, imagine that you&#39;ve written a click event handler that uses the <a href="https://developer.mozilla.org/en-US/docs/Web/API/History">History API</a> to rewrite the location bar, and implement custom single-page navigations. It might look something like:</p><pre>document.querySelector(&#39;#foo&#39;).addEventListener(&#39;click&#39;, event =&gt; {<br>  event.preventDefault();<br>  // ...call history.pushState(), use client-side rendering, etc.... });</pre><p>Your custom logic might work as intended when triggered by a mouse’s primary button, but if that code runs when a middle button is clicked, it’s effectively a false positive. Prior to the new behavior, you’d end up preventing the default action of opening a new tab, which runs counter to your user’s expectations. While you could explicitly check for event.button === 0 at the start of your handler, and only execute the code if that&#39;s the case, it&#39;s easy to forget, or never realize it&#39;s necessary to do that.</p><h3>Only run the code you need</h3><p>The flip side of fewer false positives is that auxclick callbacks will only run when there&#39;s actually a non-primary mouse button clicked. If you have code that needs to, for example, calculate an appropriate destination URL before opening a new tab, you can listen for auxclick and include that logic in your callback. It won&#39;t incur the overhead of being run when the primary mouse button is clicked.</p><h3>Browser support and compatibility</h3><p>This new behavior is currently only implemented in Chrome 55. As mentioned in the <a href="https://discourse.wicg.io/t/new-event-for-non-primary-button-click/1527">initial proposal</a>, feedback (both positive and negative) from the web developer community is appreciated. Filing a <a href="https://github.com/WICG/auxclick/issues">GitHub issue</a> is the best way to share that feedback with the folks who are working on the standardization process.</p><p>In the meantime, developers don’t have to wait for auxclick to be widely available to follow some best practices for handling mouse events. If you take the time to check the value of the MouseEvent.button property at the start of your click event handler, you can ensure that you take appropriate action. The following pattern will handle primary and auxiliary clicks differently, whether or not there&#39;s native support for auxclick:</p><pre>function handlePrimaryClick(event) {<br>  // ...code to handle the primary button click...<br>}<br><br>function handleAuxClick(event) {<br>  // ...code to handle the auxiliary button click...<br>}<br><br>document.querySelector(&#39;#foo&#39;).addEventListener(&#39;click&#39;, event =&gt; {<br>  if (event.button === 0) {<br>    return handlePrimaryClick(event);<br>  }<br><br>  // This provides fallback behavior in browsers without auxclick.<br>  return handleAuxClick(event);<br>});<br><br>// Explicitly listen for auxclick in browsers that support it.<br>document.querySelector(&#39;#foo&#39;)<br>  .addEventListener(&#39;auxclick&#39;, handleAuxClick);</pre><p><em>Originally published at </em><a href="https://developers.google.com/web/updates/2016/10/auxclick"><em>developers.google.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c8b0ad5a922" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dev-channel/auxclick-is-coming-to-chrome-55-c8b0ad5a922">auxclick is Coming to Chrome 55</a> was originally published in <a href="https://medium.com/dev-channel">Dev Channel</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Once Upon an Event Listener]]></title>
            <link>https://medium.com/dev-channel/once-upon-an-event-listener-f516bca519e6?source=rss-3fb739097395------2</link>
            <guid isPermaLink="false">https://medium.com/p/f516bca519e6</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[chrome]]></category>
            <dc:creator><![CDATA[Jeffrey Posnick]]></dc:creator>
            <pubDate>Thu, 20 Oct 2016 17:03:07 GMT</pubDate>
            <atom:updated>2016-10-21T19:11:35.235Z</atom:updated>
            <content:encoded><![CDATA[<p>Pop quiz: what’s the purpose of the third parameter passed to <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a>?</p><p>Don’t be embarrassed if you thought that addEventListener() only took two parameters, or perhaps just always hardcode a value of false, with a vague understanding that it has something to do with… bubbles?</p><p><em>The third parameter has historically been a boolean value, which defaulted to </em><em>false, and which </em><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters"><em>controlled</em></a><em> whether the event listener was in </em><a href="http://stackoverflow.com/a/4616720/385997"><em>capturing or bubbling mode</em></a><em>.</em></p><h3>A more configurable addEventListener()</h3><p>The addEventListener() method has come a long way since the early days of the web, and its new functionality is configured via a supercharged version of that third parameter. Recent changes to the <a href="https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener">method&#39;s definition</a> allow developers to provide additional options via a <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters">configuration object</a>, while remaining backward compatible when there&#39;s a boolean parameter or when an option is not specified.</p><p>We’re happy to announce that Chrome 55 adds support for the once option in that configuration object, alongside the passive (<a href="https://developers.google.com/web/updates/2016/06/passive-event-listeners">implemented in Chrome 51</a>) and capture options (<a href="https://www.chromestatus.com/feature/5718574840676352">implemented in Chrome 49</a>). For example:</p><pre>element.addEventListener(&#39;click&#39;, myClickHandler, {<br>  once: true,<br>  passive: true,<br>  capture: true<br>});</pre><p>You can mix and match those options as appropriate to your own use case.</p><h3>The benefits of cleaning up after yourself</h3><p>So that’s the syntax for using the new once option, but what does that get you? In short, it gives you an event listener that&#39;s tailored to &quot;one and done&quot; use cases.</p><p>By default, event listeners persist after the first time they’re called, which is what you want for some types of events — buttons that can be clicked multiple times, for example. For other uses, though, having an event listener stick around isn’t necessary, and can lead to undesirable behavior if you have a callback that must only execute once. Hygienic developers have always had the option of using removeEventListener() to explicitly clean things up, following patterns like:</p><pre>element.addEventListener(&#39;click&#39;, function cb(event) {<br>  // ...one-time handling of the click event...<br>  event.currentTarget.removeEventListener(event.type, cb);<br>});</pre><p>The equivalent code, making use of the new once parameter, is cleaner, and doesn&#39;t force you to keep track of the name of the event (event.type, in the previous example) or a reference to the callback function (cb):</p><pre>element.addEventListener(&#39;click&#39;, function(event) {<br>  // ...one-time handling of the click event...<br>}, {once: true});</pre><p>Cleaning up your event handlers can also offer memory efficiencies by destroying the scope that’s associated with the callback function, allowing any variables <a href="https://robertnyman.com/2008/10/09/explaining-javascript-scope-and-closures/">captured in that scope</a> to be garbage collected. Here’s one such example where it would make a difference:</p><pre>function setUpListeners() {<br>  var data = [&#39;one&#39;, &#39;two&#39;, &#39;...etc.&#39;];<br>  window.addEventListener(&#39;load&#39;, function() {<br>    doSomethingWithSomeData(data);<br>    // data is now part of the callback&#39;s scope.<br>  });<br>}</pre><p>By default, the load event listener callback will remain in scope when it finishes running, even though it&#39;s never used again. Because the data variable is used inside the callback, it will also remain in scope, and never get garbage collected. If the callback were removed via the once parameter, though, both the function itself and anything that&#39;s kept alive via its scope will potentially be candidates for garbage collection.</p><h3>Browser support</h3><p>Chrome 55+, Firefox 50+, and Safari’s <a href="https://webkit.org/blog/6640/release-notes-for-safari-technology-preview-release-7/">technology preview 7+</a> have native <a href="http://caniuse.com/#feat=once-event-listener">support</a> for the once option.</p><p>Many JavaScript UI libraries provide convenience methods for creating event listeners, and some have shortcuts for defining one-time events — the most notable of which is jQuery’s <a href="http://api.jquery.com/one/">one() method</a>. A polyfill is also available, as part of <a href="https://twitter.com/WebReflection">Andrea Giammarchi</a>’s <a href="https://github.com/WebReflection/dom4">dom4 library</a>.</p><p><em>Originally published at </em><a href="https://developers.google.com/web/updates/2016/10/addeventlistener-once"><em>developers.google.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f516bca519e6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dev-channel/once-upon-an-event-listener-f516bca519e6">Once Upon an Event Listener</a> was originally published in <a href="https://medium.com/dev-channel">Dev Channel</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Cross-origin Service Workers: Experimenting with Foreign Fetch]]></title>
            <link>https://medium.com/dev-channel/cross-origin-service-workers-experimenting-with-foreign-fetch-d7b0f5659108?source=rss-3fb739097395------2</link>
            <guid isPermaLink="false">https://medium.com/p/d7b0f5659108</guid>
            <category><![CDATA[cors]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[service-worker]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Jeffrey Posnick]]></dc:creator>
            <pubDate>Fri, 16 Sep 2016 13:44:56 GMT</pubDate>
            <atom:updated>2016-09-16T14:25:58.902Z</atom:updated>
            <content:encoded><![CDATA[<h3>Background</h3><p><a href="https://developers.google.com/web/fundamentals/primers/service-worker/">Service workers</a> give web developers the ability to respond to network requests made by their web applications, allowing them to continue working even while offline, <a href="https://developers.google.com/web/fundamentals/performance/poor-connectivity/lie-fi?hl=en">fight lie-fi</a>, and implement complex cache interactions like <a href="https://jakearchibald.com/2014/offline-cookbook/#stale-while-revalidate">stale-while-revalidate</a>. But service workers have historically been tied to a specific origin — as the owner of a web app, it’s your responsibility to write and deploy a service worker to intercept all the network requests your web app makes. In that model, each service worker is responsible for handling even cross-origin requests, for example to a third-party API or for web fonts.</p><p>What if a third-party provider of an API, or web fonts, or other commonly used service had the power to deploy their own service worker that got a chance to handle requests made by <em>other</em> origins to their origin? Providers could implement their own custom networking logic, and take advantage of a single, authoritative <a href="https://developer.mozilla.org/en-US/docs/Web/API/Cache">cache instance</a> for storing their responses. Now, thanks to <a href="https://github.com/w3c/ServiceWorker/blob/master/foreign_fetch_explainer.md">foreign fetch</a>, that type of third-party service worker deployment is a reality.</p><p>Deploying a service worker that implements foreign fetch makes sense for any provider of a service that’s accessed via HTTPS requests from browsers — just think about scenarios in which you could provide a network-independent version of your service, in which browsers could take advantage of a common resource cache. Services that could benefit from this include, but are not limited to:</p><ul><li>API providers with <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">RESTful</a> interfaces</li><li>Web font providers</li><li>Analytics providers</li><li>Image hosting providers</li><li>Generic content delivery networks</li></ul><p>Imagine, for instance, that you’re an analytics provider. By deploying a foreign fetch service worker, you can ensure that all requests to your service that fail while a user is offline are queued and replayed once connectivity returns. While it’s been possible for a service’s clients to <a href="https://developers.google.com/web/updates/2016/07/offline-google-analytics">implement similar behavior</a> via first-party service workers, requiring each and every client to write bespoke logic for your service is not as scalable as relying on a shared foreign fetch service worker that you deploy.</p><h3>Prerequisites</h3><h4>Origin Trial token</h4><p>Foreign fetch is still considered experimental. In order to keep from <a href="https://infrequently.org/2015/08/doing-science-on-the-web/">prematurely</a> baking this design in before it’s fully specified and agreed upon by browser vendors, it’s been implemented in Chrome 54 as an <a href="https://github.com/jpchase/OriginTrials/blob/gh-pages/developer-guide.md">Origin Trial</a>. As long as foreign fetch remains experimental, to use this new feature with the service you host, you’ll need to <a href="https://github.com/jpchase/OriginTrials/blob/gh-pages/developer-guide.md#how-do-i-enable-an-experimental-feature-on-my-origin">request a token</a> that’s scoped to your service’s specific origin. The token should be included as an HTTP response header in all cross-origin requests for resources that you want to handle via foreign fetch, as well as in the response for your service worker JavaScript resource:</p><pre>Origin-Trial: token_obtained_from_signup</pre><p>The trial will end in March 2017. By that point, we expect to have figured out any changes necessary to stabilize the feature, and (hopefully) enable it by default. If foreign fetch is not enabled by default by that time, the functionality tied to existing Origin Trial tokens will stop working.</p><p>To facilitate experimenting with foreign fetch prior to registering for an official Origin Trial token, you can bypass the requirement in Chrome for your local computer by going to <strong>chrome://flags/#enable-experimental-web-platform-features</strong> and enabling the “Experimental Web Platform features” flag. Please note that this needs to be done in every instance of Chrome that you want to use in your local experimentations, whereas with an Origin Trial token the feature will be available to all of your Chrome users.</p><h4>HTTPS</h4><p>As with all service worker deployments, the web server you use for serving both your resources and your service worker script <a href="https://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features">needs to be accessed via HTTPS</a>. Additionally, foreign fetch interception only applies to requests that originate from pages hosted on secure origins, so the clients of your service need to use HTTPS to take advantage of your foreign fetch implementation.</p><h3>Using Foreign Fetch</h3><p>With the prerequisites out of the way, let’s dive into the technical details needed to get a foreign fetch service worker up and running.</p><h4>Registering your service worker</h4><p>The first challenge that you’re likely to bump into is how to register your service worker. If you’ve worked with service workers before, you’re probably familiar with the following:</p><pre><em>// You can&#39;t do this!</em><br><strong>if</strong> (&#39;serviceWorker&#39; <strong>in</strong> navigator) {<br>  navigator.serviceWorker.register(&#39;service-worker.js&#39;);<br>}</pre><p>This JavaScript code for a first-party service worker registration makes sense in the context of a web app, triggered by a user navigating to a URL you control. But it’s not a viable approach to registering a third-party service worker, when the only interaction the browser will have with your server is requesting a specific subresource, not a full navigation. If the browser requests, say, an image from a CDN server that you maintain, you can’t prepend that snippet of JavaScript to your response and expect that it will be run. A different method of service worker registration, outside the normal JavaScript execution context, is required.</p><p>The solution comes in the form of an <a href="https://w3c.github.io/ServiceWorker/spec/service_worker/index.html#link-type-serviceworker">HTTP header</a> that your server can include in any response:</p><pre>Link: &lt;/service-worker.js&gt;; rel=&quot;serviceworker&quot;; scope=&quot;/&quot;</pre><p>Let’s break down that example header into its components, each of which is separated by a ; character.</p><ul><li>&lt;/service-worker.js&gt; is required, and is used to specify the path to your service worker file (replace /service-worker.js with the appropriate path to your script). This corresponds directly to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register#Parameters">scriptURL string</a> that would otherwise be passed as the first parameter to navigator.serviceWorker.register(). The value needs to be enclosed in &lt;&gt; characters (as required by the <a href="https://tools.ietf.org/html/rfc5988#section-5">Link header specification</a>), and if a relative rather than absolute URL is provided, it will be interpreted as being <a href="https://tools.ietf.org/html/rfc3986#section-5.1.3">relative to the location of the response</a>.</li><li>rel=”serviceworker” is also required, and should be included without any need for customization.</li><li>scope=”/” is an optional scope declaration, equivalent to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register#Parameters">options.scope</a> string you can pass in as the second parameter to navigator.serviceWorker.register(). For many use cases, you’re <a href="http://stackoverflow.com/a/33881341/385997">fine with using the default scope</a>, so feel free to leave this out unless you know that you need it. The same restrictions around maximum allowed scope, along with the ability to relax those restrictions via the <a href="https://w3c.github.io/ServiceWorker/spec/service_worker/#service-worker-allowed">Service-Worker-Allowed</a> header, apply to Link header registrations.</li></ul><p>Just like with a “traditional” service worker registration, using the Link header will install a service worker that will be used for the <em>next</em> request made against the registered scope. The body of the response that includes the special header will be used as-is, and is available to the page immediately, without waiting for the foreign service worker to finish installation.</p><p>Remember that foreign fetch is currently implemented as an <a href="https://github.com/jpchase/OriginTrials/blob/gh-pages/developer-guide.md">Origin Trial</a>, so alongside your Link response header, you’ll need to include a valid Origin-Trial header as well. The minimum set of response headers to add in order to register your foreign fetch service worker is</p><pre>Link: &lt;/service-worker.js&gt;; rel=&quot;serviceworker&quot;<br>Origin-Trial: token_obtained_from_signup</pre><p><strong>Note:</strong> Astute readers of the service worker specification may have <a href="https://w3c.github.io/ServiceWorker/spec/service_worker/index.html#example-00a7acb9">noticed</a> another means of performing service worker registration, via a &lt;link rel=”serviceworker”&gt; DOM element. Support for &lt;link&gt;-based registration in Chrome is currently controlled by the same Origin Trial as the Link header, so it is not yet enabled by default. &lt;link&gt;-based registration has the same limitations as JavaScript-based registration when it comes to foreign fetch registration, so for the purposes of this article, the Link header is what you should be using.</p><h4>Debugging Registration</h4><p>During development, you’ll probably want to confirm that your foreign fetch service worker is properly installed and processing requests. There are a few things you can check in Chrome’s <a href="https://developer.chrome.com/devtools">Developer Tools</a> to confirm that things are working as expected.</p><h4>Are the proper response headers being sent?</h4><p>In order to register the foreign fetch service worker, you need to set a Link header on a response to a resource hosted on your domain, as described earlier in this post. During the Origin Trial period, and assuming you don’t have <strong>chrome://flags/#enable-experimental-web-platform-features</strong> set, you also need to set a Origin-Trial response header. You can confirm that your web server is setting those headers by looking at the entry in the <a href="https://developers.google.com/web/tools/chrome-devtools/profile/network-performance/resource-loading">Network panel</a> of DevTools:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*OjJVAjOX78vfamvz.png" /></figure><h4>Is the foreign fetch service worker properly registered?</h4><p>You can also confirm the underlying service worker registration, including its scope, by looking at the full list of service workers in the <a href="https://developers.google.com/web/tools/chrome-devtools/iterate/manage-data/?hl=en">Application panel</a> of DevTools. Make sure to select the “Show all” option, since by default, you’ll only see service workers for the current origin.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*zUCvCThfhKrS0sIG.png" /></figure><h3>The install event handler</h3><p>Now that you’ve registered your third-party service worker, it will get a chance to respond to the install and activate events, just like any other service worker would. It can take advantage of those events to, for example, populate caches with required resources during the install event, or prune out-of-date caches in the activate event.</p><p>Beyond normal install event caching activities, there’s an additional step that’s <strong>required</strong> inside your third-party service worker’s install event handler. Your code needs to call registerForeignFetch(), as in the following example:</p><pre>self.addEventListener(&#39;install&#39;, event <strong>=&gt;</strong> {<br>  event.registerForeignFetch({<br>    scopes<strong>:</strong> [self.registration.scope], <em>// or some sub-scope</em><br>    origins<strong>:</strong> [&#39;*&#39;] <em>// or [&#39;https://example.com&#39;]</em><br>  });<br>});</pre><p>There are two configuration options, both required:</p><ul><li><strong>scopes</strong> takes an array of one or more strings, each of which represents a scope for requests that will trigger a foreignfetch event. <em>But wait</em>, you may be thinking, <em>I’ve already defined a scope during service worker registration!</em> That’s true, and that overall scope is still relevant — each scope that you specify here must be either equal to or a sub-scope of the service worker’s overall scope. The additional scoping restrictions here allow you to deploy an all-purpose service worker that can handle both first-party fetch events (for requests made from your own site) and third-party foreignfetch events (for requests made from other domains), and make it clear that only a subset of your larger scope should trigger foreignfetch. In practice, if you’re deploying a service worker dedicated to handling only third-party, foreignfetch events, you’re just going to want to use a single, explicit scope that’s equal to your service worker’s overall scope. That’s what the example above will do, using the value self.registration.scope.</li><li><strong>origins</strong> also takes an array of one or more strings, and allows you to restrict your foreignfetch handler to only respond to requests from specific domains. For example, if you explicitly whitelist ‘https://example.com’, then a request made from a page hosted at <a href="https://example.com/path/to/page.html">https://example.com/path/to/page.html</a> for a resource served from your foreign fetch scope will trigger your foreign fetch handler, but requests made from <a href="https://random-domain.com/path/to/page.html">https://random-domain.com/path/to/page.html</a> won’t trigger your handler. Unless you have a specific reason to only trigger your foreign fetch logic for a subset of remote origins, you can just specify ‘*’ as the only value in the array, and all origins will be whitelisted.</li></ul><h3>The foreignfetch event handler</h3><p>Now that you’ve installed your third-party service worker and it’s been configured via registerForeignFetch(), it will get a chance to intercept cross-origin <a href="https://fetch.spec.whatwg.org/#subresource-request">subresource requests</a> to your server that fall within the foreign fetch scope.</p><p><strong>Note:</strong> There’s an additional restriction in Chrome’s current implementation: only <a href="https://fetch.spec.whatwg.org/#cors-safelisted-method">GET, POST, or HEAD</a> requests that contain only <a href="https://fetch.spec.whatwg.org/#cors-safelisted-method">CORS-safelisted headers</a> are eligible for foreign fetch. This restriction is not part of the foreign fetch specification and may be relaxed in future versions of Chrome.</p><p>In a traditional, first-party service worker, each request would trigger a <a href="https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent">fetch event</a> that your service worker had a chance to respond to. Our third-party service worker is given a chance to handle a slightly different event, named foreignfetch. Conceptually, the two events are quite similar, and they give you the opportunity to inspect the incoming request, and optionally provide a response to it via respondWith():</p><pre>self.addEventListener(&#39;foreignfetch&#39;, event <strong>=&gt;</strong> {<br>  <em>// Assume that requestLogic() is a custom function that takes</em><br>  <em>// a Request and returns a Promise which resolves with a Response.</em><br>  event.respondWith(<br>    requestLogic(event.request).then(response <strong>=&gt;</strong> {<br>      <strong>return</strong> {<br>        response<strong>:</strong> response,<br>        <em>// Omit to origin to return an opaque response.</em><br>        <em>// With this set, the client will receive a CORS response.</em><br>        origin<strong>:</strong> event.origin,<br>        <em>// Omit headers unless you need additional header filtering.</em><br>        <em>// With this set, only Content-Type will be exposed.</em><br>        headers<strong>:</strong> [&#39;Content-Type&#39;]<br>      };<br>    })<br>  );<br>});</pre><p>Despite the conceptual similarities, there are a few differences in practice when calling respondWith() on a ForeignFetchEvent. Instead of just providing a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response">Response</a> (or <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise</a> that resolves with a Response) to respondWith(), like you <a href="https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent/respondWith">do with a FetchEvent</a>, you need to pass a Promise that resolves with an Object with specific properties to the ForeignFetchEvent’s respondWith():</p><ul><li>response is required, and must be set to the Response object that will be returned to the client that made the request. If you provide anything other than a valid Response, the client’s request will be terminated with a network error. Unlike when calling <a href="https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent/respondWith">respondWith()</a> inside a fetch event handler, you <strong>must</strong> provide a Response here, not a Promise which resolves with a Response! You can construct your response via a promise chain, and pass that chain as the parameter to foreignfetch’s respondWith(), but the chain must resolve with an Object that contains the response property set to a Response object. You can see a demonstration of this in the code sample above.</li><li>origin is optional, and it’s used to determine whether or not the response that’s returned is <a href="http://stackoverflow.com/questions/39109789/what-limitations-apply-to-opaque-responses">opaque</a>. If you leave this out, the response will be opaque, and the client will have limited access to the response’s body and headers. If the request was made with <a href="https://fetch.spec.whatwg.org/#concept-request-mode">mode: ‘cors’</a>, then returning an opaque response will be treated as an error. However, if you specify a string value equal to the origin of the remote client (which can be obtained via event.origin), you’re explicitly opting in to provides a CORS-enabled response to the client.</li><li>headers is also optional, and is only useful if you’re also specifying origin and returning a CORS response. By default, only headers in the <a href="https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name">CORS-safelisted response header list</a> will be included in your response. If you need to further filter what’s returned, headers takes a list of one or more header names, and it will use that as a whitelist of which headers to expose in the response. This allows you to opt-in to CORS while still preventing potentially sensitive response headers from being exposed directly to the remote client.</li></ul><p>It’s important to note that when the foreignfetch handler is run, <strong>it has access to all the credentials and </strong><a href="https://tools.ietf.org/html/rfc6454#section-8.3"><strong>ambient authority</strong></a><strong> of the origin hosting the service worker</strong>. As a developer deploying a foreign fetch-enabled service worker, it’s your responsibility to ensure that you do not leak any privileged response data that would not otherwise be available by virtue of those credentials. Requiring an opt-in for CORS responses is one step to limit inadvertent exposure, but as a developer you can explicitly make fetch() requests inside your foreignfetch handler that <a href="https://fetch.spec.whatwg.org/#concept-request-credentials-mode">do not use the implied credentials</a> via:</p><pre>self.addEventListener(&#39;foreignfetch&#39;, event <strong>=&gt;</strong> {<br>  <em>// The new Request will have credentials omitted by default.</em><br>  const noCredentialsRequest <strong>=</strong> <strong>new</strong> Request(event.request.url);<br>  event.respondWith(<br>    <em>// Replace with your own request logic as appropriate.</em><br>    fetch(noCredentialsRequest)<br>      .<strong>catch</strong>(() <strong>=&gt;</strong> caches.match(noCredentialsRequest))<br>      .then(response <strong>=&gt;</strong> ({response}))<br>  );<br>});</pre><h3>Client considerations</h3><p>There are some additional considerations that affect how your foreign fetch service worker handles requests made from clients of your service.</p><h4>Clients that have their own first-party service worker</h4><p>Some clients of your service may already have their own first-party service worker, handling requests originating from their web app. What does this mean for your third-party, foreign fetch service worker?</p><p>The fetch handler(s) in a first-party service worker get the first opportunity to respond to all requests made by the web app, even if there’s a third-party service worker with foreignfetch enabled with a scope that covers the request. But clients with first-party service workers can still take advantage of your foreign fetch service worker!</p><p>Inside a first-party service worker, using fetch() to retrieve cross-origin resources will trigger the appropriate foreign fetch service worker. That means code like the following can take advantage of your foreignfetch handler:</p><pre><em>// Inside a client&#39;s first-party service-worker.js:</em><br>self.addEventListener(&#39;fetch&#39;, event <strong>=&gt;</strong> {<br>  <em>// If event.request is under your foreign fetch service worker&#39;s</em><br>  <em>// scope, this will trigger your foreignfetch handler.</em><br>  event.respondWith(fetch(event.request));<br>});</pre><p>Similarly, if there are first-party fetch handlers, but they don’t call event.respondWith() when handling requests for your cross-origin resource, the request will automatically “fall through” to your foreignfetch handler:</p><pre><em>// Inside a client&#39;s first-party service-worker.js:</em><br>self.addEventListener(&#39;fetch&#39;, event <strong>=&gt;</strong> {<br>  <strong>if</strong> (event.request.mode <strong>===</strong> &#39;same-origin&#39;) {<br>    event.respondWith(localRequestLogic(event.request));<br>  }<br><br>  <em>// Since event.respondWith() isn&#39;t called for<br>  // cross-origin requests, any foreignfetch handlers scoped to<br>  // the request will get a chance to provide a response.</em><br>});</pre><p>If a first-party fetch handler calls event.respondWith() but does <em>not</em> use fetch() to request a resource under your foreign fetch scope, then your foreign fetch service worker will not get a chance to handle the request.</p><h4>Clients that don’t have their own service worker</h4><p>All clients that make requests to a third-party service can benefit when the service deploys a foreign fetch service worker, even if they aren’t already using their own service worker. There is nothing specific that clients need to do in order to opt-in to using a foreign fetch service worker, as long as they’re using a browser that supports it. This means that by deploying a foreign fetch service worker, your custom request logic and shared cache will benefit many of your service’s clients immediately, without them taking further steps.</p><h4>Putting it all together: where clients look for a response</h4><p>Taking into account the information above, we can assemble a hierarchy of sources a client will use to find a response for a cross-origin request.</p><ol><li>A first-party service worker’s fetch handler (if present)</li><li>A third-party service worker’s foreignfetch handler (if present, and only for cross-origin requests)</li><li>The browser’s <a href="https://jakearchibald.com/2016/caching-best-practices/#the-service-worker-the-http-cache-play-well-together-dont-make-them-fight">HTTP cache</a> (if a fresh response exists)</li><li>The network</li></ol><p>The browser starts from the top and, depending on the service worker implementation, will continue down the list until it finds a source for the response.</p><h3>Learn more</h3><ul><li><a href="https://github.com/w3c/ServiceWorker/blob/master/foreign_fetch_explainer.md">Foreign fetch explainer</a></li><li><a href="https://googlechrome.github.io/samples/service-worker/foreign-fetch/">Sample code and live demo</a></li><li><a href="https://github.com/w3c/ServiceWorker/issues">Service worker issue tracker</a></li></ul><h3>Stay up to date</h3><p>Chrome’s implementation of the foreign fetch Origin Trial is subject to change as we address feedback from developers. We’ll keep this post up to date via inline changes, and will make note the specific changes below as they happen. We’ll also share information about major changes via the <a href="https://twitter.com/@chromiumdev">@chromiumdev</a> Twitter account.</p><p><em>Originally published at </em><a href="https://developers.google.com/web/updates/2016/09/foreign-fetch"><em>developers.google.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d7b0f5659108" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dev-channel/cross-origin-service-workers-experimenting-with-foreign-fetch-d7b0f5659108">Cross-origin Service Workers: Experimenting with Foreign Fetch</a> was originally published in <a href="https://medium.com/dev-channel">Dev Channel</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[create-react-pwa]]></title>
            <link>https://medium.com/dev-channel/create-react-pwa-7b69425ffa86?source=rss-3fb739097395------2</link>
            <guid isPermaLink="false">https://medium.com/p/7b69425ffa86</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[progressive-web-app]]></category>
            <category><![CDATA[service-worker]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Jeffrey Posnick]]></dc:creator>
            <pubDate>Sat, 20 Aug 2016 00:00:00 GMT</pubDate>
            <atom:updated>2016-08-24T01:39:51.334Z</atom:updated>
            <content:encoded><![CDATA[<h3>tl;dr</h3><p>If you’d like to turn the output of <a href="https://github.com/facebookincubator/create-react-app">create-react-app</a> into a <a href="https://developers.google.com/web/progressive-web-apps/">progressive web app</a> (PWA) with offline support, take a look at the <a href="https://github.com/jeffposnick/create-react-pwa">create-react-pwa</a> repo, and in particular, the <a href="https://github.com/jeffposnick/create-react-pwa/compare/starting-point...pwa">GitHub diff</a> of the minimal changes required.</p><h3>create-react-app…</h3><p><a href="https://facebook.github.io/react/blog/2016/07/22/create-apps-with-no-configuration.html">create-react-app</a> solves a problem that many folks (including I) have run into: how can you get started with a straightforward, client-rendered, single page React app and build process, without spending hours researching a list of (sometimes conflicting) dependencies to list in your package.json? It’s a project that came out of Facebook’s React engineering a few months back, and has since gained some significant mindshare in the web developer community.</p><h3>…and it’s limitations</h3><p>In keeping with the fewer-depencies-are-better-dependencies philosophy behind create-react-app, the maintainers <a href="https://github.com/facebookincubator/create-react-app/issues/192">appear to be vigilant</a> in the fight against scope creep, and are keeping the project lean. That’s understandable, and it’s left up to end users to decide what additional functionality to layer on top of the project’s skeleton.</p><p>By design, the core of the build process isn’t directly extensible. It’s kicked off by npm run build and consists of</p><pre>&quot;scripts&quot;: {<br>  &quot;build&quot;: &quot;react-scripts build&quot;<br>}</pre><p>But! The opportunity’s there to cleanly chain some additional commands after that initial react-scripts build, while keeping the react-scripts black box opaque, giving us the chance to modify the output of the original build process.</p><h3>An Opportunity for PWA-ification</h3><p>That gets to the heart of my investigation: what’s the easiest way to guide developers starting from a fresh create-react-app towards the end goal of deploying a <a href="https://developers.google.com/web/progressive-web-apps/">progressive web app</a>? Specifically, what would it take to add in a <a href="https://developers.google.com/web/fundamentals/primers/service-worker/">service worker</a> that provided performance benefits as well as a meaningful offline experience, as well as a <a href="https://developers.google.com/web/updates/2014/11/Support-for-installable-web-apps-with-webapp-manifest-in-chrome-38-for-Android">web app manifest</a> that contained application metadata?</p><h3>Adding a Web App Manifest</h3><p>This is just a <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest">standard</a> JSON file with fields containing metadata useful for controlling the “add to homescreen” experience (currently only on Android devices). While each developer needs to choose their metadata wisely, there’s nothing particularly challenging here from a technical perspective, and we just need to create the file and get it copied over to the build/ output directory as part of the npm run build process.</p><h3>Adding a Service Worker</h3><p>To handle the service worker, I (not surprisingly) turned to the <a href="https://github.com/GoogleChrome/sw-precache">sw-precache</a> project. When added to a build process, it generates a service worker JavaScript file that will automatically version and keep all local static files — HTML, JavaScript, CSS, images, etc. — up to date. Because <em>all</em> of the create-react-app output is local static files (there’s no server-side rendering or remote API calls by default, for example), sw-precache can handle everything we need with little configuration needed.</p><p>While I’ve traditionally used sw-precache as a JavaScript module inside of a <a href="http://gulpjs.com/">gulp build</a> process, it also sports a <a href="https://github.com/GoogleChrome/sw-precache#command-line-interface">command-line interface</a>, and it’s simple to add in the sw-precache command to the end of the npm run build script chain.</p><h3>The User Experience</h3><p>Here’s what the deployed PWA looks like on Chrome for Android, going through the steps of accepting the Add to Homescreen banner, and then launching it without a network connection.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FnV8sKoVbD5Q%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DnV8sKoVbD5Q&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FnV8sKoVbD5Q%2Fhqdefault.jpg&amp;key=d04bfffea46d4aeda930ec88cc64b87c&amp;type=text%2Fhtml&amp;schema=youtube" width="640" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/295330c0db685e8359214ea7216ac600/href">https://medium.com/media/295330c0db685e8359214ea7216ac600/href</a></iframe><h3>Try it Yourself</h3><p>The best way to visualize the changes to the build process, as well as the (small) modifications needed to the source code, is to view the <a href="https://github.com/jeffposnick/create-react-pwa/compare/starting-point...pwa">GitHub diff</a> between the starting point and the “final” PWA.</p><p>Take “final” with a grain of salt, because while you will end up with a build process that yields a progressive web app, there are many directions you might want to take the starter project, and some of those might entail a more complex service worker implementation. I’ve tried to cover <a href="https://github.com/jeffposnick/create-react-pwa#what-additional-changes-might-be-needed">some scenarios</a> in the repo’s README, including <a href="https://github.com/jeffposnick/create-react-pwa#ive-added-in-react-router-and-now-my-urls-dont-work-offline">steps to take</a> if you’re using react-router and need arbitrary URLs to work offline, as well as how you can add in <a href="https://github.com/jeffposnick/create-react-pwa#im-using-cross-origin-apis-or-resources-and-they-arent-working-while-offline">runtime caching strategies</a> for calls to third party APIs and resources.</p><h3>Beyond create-react-app</h3><p>If you’re looking for examples of PWAs build with React that go a bit beyond what create-react-app offers — for instance, that take advantage of server-side rendering or are built on third-party APIs — there are a few examples I could recommend:</p><ul><li><a href="https://github.com/GoogleChrome/sw-precache/tree/master/app-shell-demo">iFixit PWA</a> (<a href="https://ifixit-pwa.appspot.com/">live deployment</a>)</li><li><a href="https://github.com/insin/react-hn">Hacker News PWA</a> (<a href="https://react-hn.appspot.com/">live deployment</a>)</li></ul><p><em>Originally published at </em><a href="https://jeffy.info/2016/08/20/create-react-pwa.html"><em>jeffy.info</em></a><em> on August 20, 2016.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7b69425ffa86" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dev-channel/create-react-pwa-7b69425ffa86">create-react-pwa</a> was originally published in <a href="https://medium.com/dev-channel">Dev Channel</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>