<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jeff Posnick</title>
    <description>The latest articles on DEV Community by Jeff Posnick (@jeffposnick).</description>
    <link>https://dev.to/jeffposnick</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F84327%2F1241cc61-aaa9-49f2-bc8b-c23d41d76619.jpg</url>
      <title>DEV Community: Jeff Posnick</title>
      <link>https://dev.to/jeffposnick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeffposnick"/>
    <language>en</language>
    <item>
      <title>Using npm modules inside of Apps Script</title>
      <dc:creator>Jeff Posnick</dc:creator>
      <pubDate>Mon, 12 Jul 2021 04:00:00 +0000</pubDate>
      <link>https://dev.to/jeffposnick/using-npm-modules-inside-of-apps-script-1lkp</link>
      <guid>https://dev.to/jeffposnick/using-npm-modules-inside-of-apps-script-1lkp</guid>
      <description>&lt;p&gt;I recently was processing some data using &lt;a href="https://developers.google.com/apps-script"&gt;Apps Script&lt;/a&gt;, and needed to parse out &lt;a href="https://en.wikipedia.org/wiki/Second-level_domain"&gt;second-level domain&lt;/a&gt; info from a bunch of URLs. This is definitely &lt;a href="https://twitter.com/jeffposnick/status/1401218570093305863"&gt;&lt;strong&gt;not&lt;/strong&gt; the job for regular expressions&lt;/a&gt;, but it's perfect for an &lt;code&gt;npm&lt;/code&gt; module, &lt;a href="https://www.npmjs.com/package/psl"&gt;&lt;code&gt;psl&lt;/code&gt;&lt;/a&gt;, that uses the &lt;a href="https://publicsuffix.org/"&gt;public suffix list&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But while Apps Script has come a long way, and features lots of ES2015+ goodness nowadays, it's not possible to pull in arbitrary code from &lt;code&gt;npm&lt;/code&gt; and run it directly.&lt;/p&gt;

&lt;p&gt;To work around this, I created the following &lt;code&gt;index.js&lt;/code&gt; file locally, exporting the interface that I wanted to call from Apps Script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;psl&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;psl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Url&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url-parse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;parseHostname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceURL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceURL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;psl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I installed the necessary dependencies from &lt;code&gt;npm&lt;/code&gt;, and bundled this code up with &lt;a href="https://esbuild.github.io/"&gt;&lt;code&gt;esbuild&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; psl url-parse punycode
npx esbuild index.js &lt;span class="nt"&gt;--bundle&lt;/span&gt; &lt;span class="nt"&gt;--global-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;psl &lt;span class="nt"&gt;--outfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;psl.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I manually copied the contents of the &lt;code&gt;psl.js&lt;/code&gt; file into a &lt;code&gt;psl.gs&lt;/code&gt; file, alongside my main &lt;code&gt;Code.gs&lt;/code&gt; file in the Apps Script editor. (This would be annoying if the bundled output changed frequently, but doing it once by hand wasn't a problem.)&lt;/p&gt;

&lt;p&gt;Apps Script will automatically make the contents of all &lt;code&gt;.gs&lt;/code&gt; files in a project visible in the same global scope, so I could now write code like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sld&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;psl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parseHostname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;inside of my main &lt;code&gt;Code.gs&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;The Apps Script runtime environment still has a bunch of quirks when compared to Node, so don't expect all of your bundled code to work as-is. Polyfills may be needed (I used &lt;a href="https://www.npmjs.com/package/url-parse"&gt;&lt;code&gt;url-parse&lt;/code&gt;&lt;/a&gt;, for instance, since the native &lt;code&gt;URL&lt;/code&gt; object isn't available in Apps Script.)&lt;/p&gt;

&lt;p&gt;Once you copy over the bundled code to Apps Script, it's never going to be updated, so make sure you're prepared to rebundle if and when there are any security or feature updates to the modules you're using.&lt;/p&gt;

</description>
      <category>appsscript</category>
      <category>npm</category>
      <category>esbuild</category>
    </item>
    <item>
      <title>Service workers in create-react-app v4+</title>
      <dc:creator>Jeff Posnick</dc:creator>
      <pubDate>Tue, 22 Dec 2020 15:45:27 +0000</pubDate>
      <link>https://dev.to/jeffposnick/service-workers-in-create-react-app-v4-3mm0</link>
      <guid>https://dev.to/jeffposnick/service-workers-in-create-react-app-v4-3mm0</guid>
      <description>&lt;p&gt;Ever since built-in support for service workers was added to &lt;code&gt;create-react-app&lt;/code&gt; v2, developers have been asking for more control over the resulting service worker's functionality. This might mean disabling &lt;a href="https://web.dev/precache-with-workbox/"&gt;precaching&lt;/a&gt; in favor of solely &lt;a href="https://web.dev/runtime-caching-with-workbox/"&gt;runtime caching&lt;/a&gt;, or it might mean adding in &lt;a href="https://developers.google.com/web/ilt/pwa/introduction-to-push-notifications"&gt;push notification&lt;/a&gt; or &lt;a href="https://web.dev/web-share-target/"&gt;web share target&lt;/a&gt; support.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;create-react-app&lt;/code&gt; v4+ will &lt;a href="https://github.com/facebook/create-react-app/blob/282c03f9525fdf8061ffa1ec50dce89296d916bd/packages/react-scripts/config/webpack.config.js#L709-L721"&gt;check for the presence&lt;/a&gt; of a &lt;code&gt;src/service-worker.js&lt;/code&gt; file at build time, and if found, run &lt;code&gt;workbox-webpack-plugin&lt;/code&gt;'s &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin#injectmanifest_plugin"&gt;&lt;code&gt;InjectManifest&lt;/code&gt; plugin&lt;/a&gt;, passing in that file as the &lt;code&gt;swSrc&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;If you're starting a new project and follow the instruction from &lt;code&gt;create-react-app&lt;/code&gt;'s "&lt;a href="https://create-react-app.dev/docs/making-a-progressive-web-app/"&gt;Making a Progressive Web App&lt;/a&gt;" guide, i.e. you run &lt;code&gt;npx create-react-app my-app --template cra-template-pwa&lt;/code&gt;, you'll end up with everything in the right place.&lt;/p&gt;

&lt;p&gt;Which is to say your project will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automatically &lt;a href="https://developers.google.com/web/tools/workbox/guides/using-bundlers"&gt;bundle&lt;/a&gt; the code in &lt;code&gt;src/service-worker.js&lt;/code&gt; (transforming the ES module imports into code that can be run inside the service worker)&lt;/li&gt;
&lt;li&gt;look for the symbol &lt;code&gt;self.__WB_MANIFEST&lt;/code&gt; somewhere inside your &lt;code&gt;src/service-worker.js&lt;/code&gt;, and replace it with a &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-precaching#explanation_of_the_precache_list"&gt;precache manifest&lt;/a&gt;, consisting of URLs and revision info about all your &lt;code&gt;webpack&lt;/code&gt; assets, so that Workbox can precache them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're not interested in precaching your &lt;code&gt;webpack&lt;/code&gt; assets, then you don't need to use the &lt;code&gt;InjectManifest&lt;/code&gt; plugin at all, and you can just put whatever code you want in a file named &lt;em&gt;anything other than&lt;/em&gt; &lt;code&gt;src/service-worker.js&lt;/code&gt;, and register that file as your service worker. That's up to you.&lt;/p&gt;

&lt;p&gt;If you are interested in Workbox's precaching, but you're upgrading from an older &lt;code&gt;create-react-app&lt;/code&gt; and you don't have a "correct" &lt;code&gt;src/service-worker.js&lt;/code&gt; file, you can manually copy the &lt;a href="https://github.com/cra-template/pwa/blob/master/packages/cra-template-pwa/template/src/service-worker.js"&gt;file from the template&lt;/a&gt; into your project.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(This post was adapted from a &lt;a href="https://stackoverflow.com/questions/65396446/how-to-add-workbox-to-react-post-update/65399739#65399739"&gt;Stack Overflow response&lt;/a&gt;.)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>serviceworkers</category>
      <category>react</category>
      <category>pwa</category>
    </item>
    <item>
      <title>Performance auditing an eCommerce site</title>
      <dc:creator>Jeff Posnick</dc:creator>
      <pubDate>Mon, 06 Apr 2020 15:00:00 +0000</pubDate>
      <link>https://dev.to/chromiumdev/performance-auditing-an-ecommerce-site-3m8a</link>
      <guid>https://dev.to/chromiumdev/performance-auditing-an-ecommerce-site-3m8a</guid>
      <description>&lt;h2&gt;
  
  
  Alan's weekly eCommerce livestreams
&lt;/h2&gt;

&lt;p&gt;My teammate &lt;a href="https://alankent.me/"&gt;Alan&lt;/a&gt;'s been running a weekly livestream on &lt;a href="https://www.youtube.com/channel/UCyQwDaXnT7wMBBqIaAfmY7g"&gt;his YouTube channel&lt;/a&gt;, focusing on topics of interest to eCommerce websites. He asked me to come on this week and perform a site performance audit.&lt;/p&gt;

&lt;p&gt;Since I'm currently homebound due to Coronavirus concerns in NYC, I haven't had the chance to perform any &lt;a href="https://dev.to/jeffposnick/cds-perf-review-clinic-takeaways-2267"&gt;in-person site audits&lt;/a&gt; in a while. Going through the process via a livestream was a great opportunity!&lt;/p&gt;

&lt;h2&gt;
  
  
  The site review
&lt;/h2&gt;

&lt;p&gt;The review process took about 50 minutes (I'd recommend watching at 2x playback speed...) and covered some relevant findings from WebPageTest.org and Lighthouse.&lt;/p&gt;

&lt;p&gt;We choose the site, &lt;a href="https://threddies.com/"&gt;https://threddies.com/&lt;/a&gt;, "at random" based on folks who &lt;a href="https://twitter.com/akent99/status/1245466347502333952"&gt;volunteered&lt;/a&gt;. I think it ended up being fairly representative of the types of issues a lot of sites encounter.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/l4bIT3CXCpc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lightly edited from the notes I took during the review.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools used:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://webpagetest.org/easy"&gt;"Easy" mode on WebPageTest&lt;/a&gt; (&lt;a href="https://webpagetest.org/result/200402_FD_f791a96a04046aaa9d7583914ba1c952/"&gt;site results&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/tools/lighthouse"&gt;Lighthouse&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Observations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;11 seconds for main content to load — what's loaded beforehand?&lt;/li&gt;
&lt;li&gt;How many of the widgets/analytics tools can be &lt;a href="https://web.dev/native-lazy-loading/"&gt;lazily-loaded&lt;/a&gt; (or potentially removed, if appropriate)?&lt;/li&gt;
&lt;li&gt;HTTP cache expiration &lt;a href="https://web.dev/reliable/"&gt;best practices&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use the &lt;a href="https://developers.google.com/web/tools/chrome-devtools/coverage"&gt;Coverage panel&lt;/a&gt; in Chrome Dev Tools to evaluate how much of your JS/CSS in the critical request path is being used&lt;/li&gt;
&lt;li&gt;Q: Can they get away with loading Google Maps static images instead of Google Maps widget? Answer: &lt;a href="https://developers.google.com/maps/documentation/maps-static/intro"&gt;yes, they can&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Always click on the "Learn More" link in Lighthouse for additional context!&lt;/li&gt;
&lt;li&gt;Zach Leatherman's &lt;a href="https://www.zachleat.com/web/comprehensive-webfonts/"&gt;guides to loading web fonts&lt;/a&gt; are great.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>performance</category>
    </item>
    <item>
      <title>Exposing headers on CORS responses</title>
      <dc:creator>Jeff Posnick</dc:creator>
      <pubDate>Mon, 22 Jul 2019 12:00:00 +0000</pubDate>
      <link>https://dev.to/chromiumdev/exposing-headers-on-cors-responses-1k8j</link>
      <guid>https://dev.to/chromiumdev/exposing-headers-on-cors-responses-1k8j</guid>
      <description>&lt;h2&gt;
  
  
  CORS and its discontents
&lt;/h2&gt;

&lt;p&gt;The concept of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS"&gt;CORS&lt;/a&gt; requests comes up a lot in my professional life. Much of the time, it's in the context of why a given response is &lt;a href="https://stackoverflow.com/questions/39109789/what-limitations-apply-to-opaque-responses"&gt;opaque&lt;/a&gt;, and how to make that response non-opaque so that it plays nicely with service workers and the Cache Storage API.&lt;/p&gt;

&lt;p&gt;Fortunately, many popular third-party APIs and hosts support CORS nowadays, and solving your basic CORS-related mystery normally boils down to, say, adding in the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes"&gt;&lt;code&gt;crossorigin&lt;/code&gt; attribute&lt;/a&gt; to your &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;

&lt;h2&gt;
  
  
  When CORS is not enough
&lt;/h2&gt;

&lt;p&gt;But while enabling CORS is enough to get back basic information about an HTTP response—like its status code, or access to its body—there's still some information that's locked down by default. The headers exposed on a CORS response, for instance, are limited to the following six &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Simple_response_header"&gt;"simple" response headers&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Cache-Control&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content-Language&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Expires&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Last-Modified&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Pragma&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of those headers can come in handy when accessed inside of a service worker, but there's one in particular that can be useful, but isn't exposed by default: &lt;code&gt;Date&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In particular, if you're using &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-cache-expiration#restrict_the_age_of_cached_entries"&gt;Workbox's cache expiration&lt;/a&gt; logic and you provide a &lt;code&gt;maxAgeSeconds&lt;/code&gt; parameter, the &lt;code&gt;Date&lt;/code&gt; of the cached response &lt;a href="https://github.com/GoogleChrome/workbox/blob/b0825d74d81264e7b4537ed170dd60de638561ba/packages/workbox-expiration/src/Plugin.ts#L176-L195"&gt;is checked&lt;/a&gt; against the difference between the current time and &lt;code&gt;maxAgeSeconds&lt;/code&gt;. If the &lt;code&gt;Date&lt;/code&gt; is too old, then the cached response will end up being ignored.&lt;/p&gt;

&lt;p&gt;But... this logic only works if there's a &lt;code&gt;Date&lt;/code&gt; header exposed on the response. By default, that won't be the case for a CORS response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exposition
&lt;/h2&gt;

&lt;p&gt;The workaround, as with so many things related to CORS, involves fiddling with HTTP response headers. You'll either need access to the underlying HTTP server yourself, or you'll need to reach out to your CDN/API provider asking them to make the change.&lt;/p&gt;

&lt;p&gt;Setting &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers"&gt;&lt;code&gt;Access-Control-Expose-Headers: Date&lt;/code&gt;&lt;/a&gt; will permit the &lt;code&gt;Date&lt;/code&gt; response header to be visible to your web app's code, and you could include any additional headers there in a comma-separated list.&lt;/p&gt;

&lt;p&gt;If you're using your own Express-based web server, the &lt;a href="https://www.npmjs.com/package/corser"&gt;&lt;code&gt;corser&lt;/code&gt; middleware&lt;/a&gt; looks pretty reasonable for setting up a working configu ration. Their docs include a &lt;a href="https://www.npmjs.com/package/corser#getting-a-response-header-returns-refused-to-get-unsafe-header-x"&gt;recipe&lt;/a&gt; for configuring the exposed response headers.&lt;/p&gt;

</description>
      <category>cors</category>
      <category>javascript</category>
      <category>post</category>
      <category>workbox</category>
    </item>
    <item>
      <title>Paying Attention while Loading Lazily</title>
      <dc:creator>Jeff Posnick</dc:creator>
      <pubDate>Fri, 07 Jun 2019 17:53:30 +0000</pubDate>
      <link>https://dev.to/jeffposnick/paying-attention-while-loading-lazily-45ef</link>
      <guid>https://dev.to/jeffposnick/paying-attention-while-loading-lazily-45ef</guid>
      <description>&lt;h3&gt;What's all this?&lt;/h3&gt;

&lt;p&gt;
  This is an interactive project that explores what happens when loading best practices
  (hashed asset URLs, lazy-loading, service workers) meets single page app usage patterns
  (long-lived tabs, "fake" navigations via the History API) meets common deployment scenarios
  (redeployments that don't preserve old URLs).
&lt;/p&gt;

&lt;p&gt;
  If you're feeling particularly meta, you can watch a video walkthrough of this material:
&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/9Ifnvm9nWs8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;How's it work?&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Visit one of the loading scenarios below, and leave the page open.&lt;/li&gt;
  &lt;li&gt;
    In the Glitch editor, make a change to a component
    (like &lt;a href="https://glitch.com/edit/#!/pawll?path=common/About.js"&gt;&lt;code&gt;About.js&lt;/code&gt;&lt;/a&gt;)
  &lt;/li&gt;
  &lt;li&gt;
    Using the &lt;a href="https://glitch.com/help/console/"&gt;Glitch console&lt;/a&gt;,
    run &lt;code&gt;webpack --config=path/to/webpack.config.js&lt;/code&gt; to rebuild the scenario you have open.
  &lt;/li&gt;
  &lt;li&gt;You've just "redeployed!" Navigate around the open scenario page see what works (and what doesn't).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
  (Updating the web app's JavaScript &lt;a href="https://glitch.com/edit/#!/pawll?path=watch.json"&gt;won't trigger&lt;/a&gt;
  an &lt;em&gt;automatic&lt;/em&gt; rebuild, though updating
  &lt;a href="https://glitch.com/edit/#!/pawll?path=server.js"&gt;&lt;code&gt;server.js&lt;/code&gt;&lt;/a&gt; will.)
&lt;/p&gt;

&lt;h3&gt;Loading scenarios to try&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://pawll.glitch.me/1-no-hash-no-lazy/build/"&gt;No hashing, no lazy-loading&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://pawll.glitch.me/2-no-hash-lazy/build/"&gt;No hashing, with lazy-loading&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://pawll.glitch.me/3-hash-lazy/build/"&gt;Hashed asset URLs, with lazy-loading&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://pawll.glitch.me/4-hash-lazy-errorboundary/build/"&gt;Hashed asset URLs, with lazy-loading, and error boundaries&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://pawll.glitch.me/5-hash-lazy-errorboundary-sw/build/"&gt;Hashed asset URLs, with lazy-loading, error boundaries, and service worker precaching&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Learn more&lt;/h3&gt;

&lt;h4&gt;Loading JavaScript&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://developers.google.com/web/fundamentals/performance/optimizing-javascript/code-splitting/"&gt;Reduce JavaScript Payloads with Code Splitting&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://reactjs.org/docs/code-splitting.html"&gt;React's Code Spltting Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://reactjs.org/docs/error-boundaries.html"&gt;React's Error Boundary Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Service workers&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://developers.google.com/web/fundamentals/primers/service-workers/"&gt;Service Workers: an Introduction&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://developers.google.com/web/tools/workbox/"&gt;Workbox&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-precaching"&gt;Workbox's Precaching&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/facebook/create-react-app/issues/3613#issuecomment-353467430"&gt;The &lt;code&gt;create-react-app&lt;/code&gt; discussion that inspired this project&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;HTTP serving&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://jakearchibald.com/2016/caching-best-practices/"&gt;Caching best practices &amp;amp; max-age gotchas&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://blog.pshrmn.com/entry/how-single-page-applications-work/"&gt;How Single-Page Applications Work&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>lazyloading</category>
      <category>performance</category>
      <category>serviceworkers</category>
    </item>
    <item>
      <title>That time I used a generator</title>
      <dc:creator>Jeff Posnick</dc:creator>
      <pubDate>Fri, 22 Mar 2019 12:00:00 +0000</pubDate>
      <link>https://dev.to/jeffposnick/that-time-i-used-a-generator-24kg</link>
      <guid>https://dev.to/jeffposnick/that-time-i-used-a-generator-24kg</guid>
      <description>&lt;h1&gt;
  
  
  Backstory
&lt;/h1&gt;

&lt;p&gt;I moved over to work on Google's Web DevRel team way back in 2014, and one of the first tasks that I took on was writing short update articles and code samples for new web platform features. These are... somewhat embarrassing to look back on, so I won't link to many here, but one of the first I put together covered &lt;a href="https://developers.google.com/web/updates/2014/10/Generators-the-Gnarly-Bits"&gt;generators&lt;/a&gt;. I didn't have a huge amount to say about generators, so the general approach used in the article was to link to some more &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*"&gt;canonical resources&lt;/a&gt;,while calling out a couple of interesting "gotchas" that I thought could add some value.&lt;/p&gt;

&lt;p&gt;So I wrote that, moved on, and then pretty much forgot that generators existed for the next 4 years.&lt;/p&gt;

&lt;h1&gt;
  
  
  Refactoring
&lt;/h1&gt;

&lt;p&gt;That takes us to a few months ago, when I was working on a &lt;a href="https://github.com/GoogleChrome/workbox/issues/1793"&gt;rewrite&lt;/a&gt;of the &lt;code&gt;workbox-precaching&lt;/code&gt; module as part of the &lt;a href="https://github.com/GoogleChrome/workbox/releases/tag/v4.0.0"&gt;Workbox v4&lt;/a&gt;release. This gave me an opportunity to revisit some code that hadn't been touched in a while.&lt;/p&gt;

&lt;h2&gt;
  
  
  Original logic
&lt;/h2&gt;

&lt;p&gt;The actual code isn't super-relevant (I'll link to the before and after below, for those who are interested), but the main points were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It was code to compare a string against few possible matches.&lt;/li&gt;
&lt;li&gt;Calculating each possible match is (somewhat) expensive.&lt;/li&gt;
&lt;li&gt;The code returned &lt;code&gt;true&lt;/code&gt; as soon as it found a match.&lt;/li&gt;
&lt;li&gt;If none of the conditions matched, it returned &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The original code looked something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlToMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlVariation1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;generateVariation1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlToMatch&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;urlVariation1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlVariation2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;generateVariation2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlToMatch&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;urlVariation2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// etc.&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm not a huge fan of a repeated sequence of &lt;code&gt;if(...) { return ... }&lt;/code&gt; statements aesthetically, and structuring code like that can make it harder to understand that each test case is effectively doing the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactoring without generators
&lt;/h2&gt;

&lt;p&gt;One potential refactoring to emphasize the repeated logic could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlToMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlVariations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;generateVariation1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;generateVariation2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// etc.&lt;/span&gt;
&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;generateVariation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;generateVariation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlVariation&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;urlVariations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlToMatch&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;urlVariation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like that version of the code from an aesthetic point of view, but one downside is that you end up running each of the &lt;code&gt;generateVariationN()&lt;/code&gt; functions ahead of time. If a variation early in the list ends up matching, you've ended up running (potentially expensive) code for nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactoring with generators
&lt;/h2&gt;

&lt;p&gt;So! This is when I remembered that generators were A Thing, and could come in handy in this use case.&lt;/p&gt;

&lt;p&gt;Generators are &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators#Generator_functions"&gt;iterable&lt;/a&gt;,so it could be dropped right in to a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of"&gt;&lt;code&gt;for...of&lt;/code&gt;&lt;/a&gt; loop.&lt;/p&gt;

&lt;p&gt;Generators only run when their &lt;code&gt;next()&lt;/code&gt; value is requested: they'll execute until a &lt;code&gt;yield&lt;/code&gt; keyword is encountered, at which point they pause and control goes back to whatever triggered the iteration. If we yield the results of our potentially expensive functions one at a time inside of a generator, we don't have to worry about executing functions whose results won't actually be needed. And we still get to structure the code that uses the values as a loop rather than a sequence of &lt;code&gt;if(...) { return ...; }&lt;/code&gt; statements. It's the best of both worlds!&lt;/p&gt;

&lt;p&gt;Using a generator and a &lt;code&gt;for...of&lt;/code&gt; loop gives us code that looks something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;generateVariations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// You could put these yields inside a loop, too!&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;generateVariation1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;generateVariation2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// etc.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlVariation&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;generateVariations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlToMatch&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;urlVariation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The actual changes in Workbox
&lt;/h2&gt;

&lt;p&gt;If you're curious, the original code in Workbox v3 is&lt;a href="https://github.com/GoogleChrome/workbox/blob/d27aafbdf164f051a883965058e6eb4c0df3a052/packages/workbox-precaching/_default.mjs#L76-L130"&gt;here&lt;/a&gt;.The v4 code has been split up into modules for the new&lt;a href="https://github.com/GoogleChrome/workbox/blob/f1164254b8abdd12c5c601ee7e7fc7d73fffd979/packages/workbox-precaching/utils/generateURLVariations.mjs#L23-L55"&gt;generator&lt;/a&gt;and the&lt;a href="https://github.com/GoogleChrome/workbox/blob/f1164254b8abdd12c5c601ee7e7fc7d73fffd979/packages/workbox-precaching/utils/getCacheKeyForURL.mjs#L25-L35"&gt;code that loops over the generated values&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>generators</category>
      <category>refactoring</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CDS Perf Review Clinic Takeaways</title>
      <dc:creator>Jeff Posnick</dc:creator>
      <pubDate>Fri, 10 Nov 2017 12:00:00 +0000</pubDate>
      <link>https://dev.to/jeffposnick/cds-perf-review-clinic-takeaways-2267</link>
      <guid>https://dev.to/jeffposnick/cds-perf-review-clinic-takeaways-2267</guid>
      <description>&lt;h1&gt;
  
  
  CDS '17 Retrospective
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://developer.chrome.com/devsummit/"&gt;Chrome Dev Summit 2017&lt;/a&gt; is behind us. I spent most of the run-up to the event putting together my&lt;a href="https://www.youtube.com/watch?v=DtuJ55tmjps"&gt;"Workbox: Flexible PWA Libraries" talk&lt;/a&gt;, but beyond that, I was also involved in &lt;a href="https://twitter.com/jeffposnick/status/922899094053330944"&gt;running on-site Performance Review Clinics&lt;/a&gt; that attendees could sign up for.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h1&gt;
  
  
  Linkdump!
&lt;/h1&gt;

&lt;p&gt;Huge thanks go out to all the authors, many of whom I'm lucky enough to work with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overall Migration/End to End Case Study
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/dev-channel/treebo-a-react-and-preact-progressive-web-app-performance-case-study-5e4f450d5299"&gt;A React And Preact Progressive Web App Performance Case Study: Treebo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Loading and Prioritization
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/fundamentals/performance/http2/"&gt;Introduction to HTTP/2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/fundamentals/performance/resource-prioritization"&gt;Resource Prioritization – Getting the Browser to Help You&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.html5rocks.com/en/tutorials/speed/script-loading/"&gt;Deep dive into the murky waters of script loading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  JavaScript
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://infrequently.org/2017/10/can-you-afford-it-real-world-web-performance-budgets/"&gt;Can You Afford It?: Real-world Web Performance Budgets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/reloading/javascript-start-up-performance-69200f43b201"&gt;JavaScript Start-up Performance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CSS
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/addyosmani/critical"&gt;&lt;code&gt;critical&lt;/code&gt;: Extract &amp;amp; Inline Critical-path CSS in HTML pages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jakearchibald.com/2016/link-in-body/"&gt;The future of loading CSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/filamentgroup/loadCSS/"&gt;&lt;code&gt;loadCss&lt;/code&gt;: A function for loading CSS asynchronously&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Caching
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching"&gt;HTTP Caching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jakearchibald.com/2016/caching-best-practices/"&gt;Caching best practices &amp;amp; max-age gotchas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/fundamentals/primers/service-workers/"&gt;Service Workers: an Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/tools/workbox/"&gt;Workbox: JavaScript libraries for Progressive Web Apps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Images/Multimedia Content
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://images.guide/"&gt;Essential Image Optimization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://deanhume.com/home/blogpost/lazy-loading-images-using-intersection-observer/10163"&gt;Lazy Loading Images Using Intersection Observer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/icon-fonts-vs-svg/"&gt;Inline SVG vs Icon Fonts [CAGEMATCH]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/content-jumping-avoid/"&gt;Content Jumping (and How To Avoid It)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.labnol.org/internet/light-youtube-embeds/27941/"&gt;A Better Method for Embedding YouTube Videos on your Website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>performance</category>
      <category>perf</category>
      <category>linkdump</category>
    </item>
  </channel>
</rss>
