{"id":2177,"date":"2025-10-28T20:13:18","date_gmt":"2025-10-29T01:13:18","guid":{"rendered":"https:\/\/www.phpied.com\/?p=2177"},"modified":"2025-10-29T12:11:29","modified_gmt":"2025-10-29T17:11:29","slug":"import-javascript-like-its-2026","status":"publish","type":"post","link":"https:\/\/www.phpied.com\/import-javascript-like-its-2026\/","title":{"rendered":"Import JavaScript like it&#8217;s 2026"},"content":{"rendered":"<p><script src=\"\/run_prettify.js\" defer=\"\"><\/script><br \/>\nI started this new project called sightread.org to generate music to practice sight reading. (Still early days, it works with rhythms only). I wanted to go for no build process and modern JS and modern HTML.<\/p>\n<h2>How modern is modern?<\/h2>\n<p>IE8? IE10? In my head when I think \"modern\" it always brings an image of polyfills, transpilation and so on to make sure my code works \"everywhere\". But I think this is antiquated thinking. Most people browse with very capable user agents and there's little reason to send subpar (transpiled) code to them. Where I draw the line of \"modernity\" is Safari 15.3. Just because I want my code to work on my <i>d\u00e9mod\u00e9<\/i> iPhone 8 which can no longer receive updates and is stuck on this browser version.<\/p>\n<p>Turns out what I want to accomplish is perfectly supported, except for the <code>dialog<\/code> HTML element. For this, I'll use a polyfill. For everything else - raw JavaScript!<\/p>\n<h2>Imports<\/h2>\n<p>We know CSS <code>@imports<\/code> are bad for performance because they reduce parallel downloads. The browser fetches <code>a.css<\/code>, finds out it imports <code>b.css<\/code> and goes to fetch <code>b.css<\/code>.<\/p>\n<p>JavaScript is no different. Here's my initial and na\u00efve implementation:<\/p>\n<pre class=\"prettyprint\">&lt;script src=\"abcjs-basic-min.js\"&gt;&lt;\/script&gt;\n&lt;script type=\"module\"&gt;\n  import { App } from '.\/app.js';\n  new App();\n&lt;\/script&gt;\n<\/pre>\n<p>Here <code>abcjs-basic-min.js<\/code> is a library for music notation, I need its <code>window.ABCJS<\/code> global in my <code>App<\/code>, so it's loaded synchronously. <code>App<\/code> imports other modules, which have their own dependencies and so on. But look what's happening:<br \/>\n<img decoding=\"async\" src=\"\/files\/blogimages\/import2026\/before.png\"><\/p>\n<p><code>abcjs<\/code>, being synchronous, blocks the rest of the downloads. Then <code>app.js<\/code> is loaded, then the browser discovers its dependencies and loads these as well. Two-step blocking, not cool.<\/p>\n<h2>Solution<\/h2>\n<p>Step 1: defer abcjs. This way the browser can move on and discover the other scripts. But <code>defer<\/code> means still execute in order, so <code>App<\/code> has access to the global <code>ABCJS<\/code>.<br \/>\nStep 2: import the dependencies. The browser can then discover and load them in parallel, before <code>app.js<\/code> is loaded.<\/p>\n<pre class=\"prettyprint\">&lt;script src=\"abcjs-basic-min.js\" defer&gt;&lt;\/script&gt;\n&lt;script type=\"module\"&gt;\n  import { App } from '.\/app.js';\n  import '.\/lib.js';\n  import '.\/rossini.js';\n  import '.\/abchelpers.js';\n  new App();\n&lt;\/script&gt;\n<\/pre>\n<p>And look at all the parallelization:<br \/>\n<img decoding=\"async\" src=\"\/files\/blogimages\/import2026\/after.png\"><\/p>\n<p>Even while <code>abcjs<\/code> (the largest download) is still pending, everything else is ready:<\/p>\n<p><img decoding=\"async\" src=\"\/files\/blogimages\/import2026\/after-mid.png\"><\/p>\n<p>Also note that <code>customize.js<\/code> is not even loaded. That's because it's responsible for a dialog and only loaded when the dialog is needed. Fancy dynamic imports, eh?<\/p>\n<pre class=\"prettyprint\">const { openCustomDialog } = await import('.\/customize.js');\n<\/pre>\n<p>The only drawback is repetitive <code>import<\/code>-ing. But is it really that bad having to think for a second if a file is needed for the initial rendering? I personally don't think so.<\/p>\n<p>Update: A <a href=\"https:\/\/sightread.org\/index-sync.html\">test page<\/a> showing that just removing the <code>defer<\/code> attribute and making the first script synchronous effectively disables the browsers' preload scanner from discovering the <code>import<\/code>s. And this is consistent in FF, Chrome, Safari. Thanks for the idea for the follow up demonstration to <a href=\"https:\/\/bsky.app\/profile\/programmingart.bsky.social\">Robin Marx<\/a>!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I started this new project called sightread.org to generate music to practice sight reading. (Still early days, it works with rhythms only). I wanted to go for no build process and modern JS and modern HTML. How modern is modern? IE8? IE10? In my head when I think &#8220;modern&#8221; it always brings an image of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[5,43],"tags":[],"_links":{"self":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/posts\/2177"}],"collection":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/comments?post=2177"}],"version-history":[{"count":0,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/posts\/2177\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/media?parent=2177"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/categories?post=2177"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/tags?post=2177"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}