In practice, you are likely to have to contort your code very significantly to use dynamic imports pervasively. And pervasive usage such as you seem to be describing is emphatically not how dynamic imports were intended to be used, and wouldn’t speed things up much in general anyway (because you still can’t do anything until your dependencies have loaded), and stands a good chance of being slower.
> Use dynamic import only when necessary. The static form is preferable for loading initial dependencies, and can benefit more readily from static analysis tools and tree shaking.
(Also, a quibble over your wording: dynamic imports aren’t a statement, but a call, though not a function call.)
You need to develop with this in mind from the start. I am not suggesting refactoring an existing application to use it for every single dependency as yes that would be a pain.
Instead import your initial generic dependencies up-front as usual, but then notionally break your application up by CUJ and load the CUJ-specific code dynamically as needed.
E.g. for a hypothetical email client you would import the initial "inbox view" code upfront (along with generic common dependencies), but you might not import the code needed for a rich text editor until the user clicks on the "write email" button, saving that network load and code execution for when - or indeed if - the user actually needs it.
Given the context (waterfall stuff), I was assuming you were describing pervasive dynamic imports, since the selective use of dynamic imports for code splitting is fairly standard practice. (And indeed, I know from working on it a few years back and doing most of the porting from a custom module system to ECMAScript modules, Fastmail’s webmail works in this way, though my recollection is that compose specifically is preloaded once the rest of the mail stuff is done, so that when you hit that compose button it’s almost certainly ready to go common fault of code splitting is deferring loading too long, so that you end up slowing users down.)
When I spoke of unused code, I was meaning things like a file containing a bunch of functions, of which you only use one, and so there are the other functions and perhaps deeper imports that weren’t actually necessary; a bundler would remove all the unused stuff. The “solution” in such a case would be to do something like splitting each function into its own file, but that’s worse than before because now your import chains are probably even longer, and even more files have to be loaded (and there is still some filewise overhead, even if HTTP/2 improves it drastically).
I’ll temper my expressed position slightly by saying that bundling is not so significantly advantageous if you have written all of the code (you’re not importing third-party libraries), and keep your file count fairly low and import depth very low. But at that point, perhaps you should have just dropped it all in one file.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... expresses it well:
> Use dynamic import only when necessary. The static form is preferable for loading initial dependencies, and can benefit more readily from static analysis tools and tree shaking.
(Also, a quibble over your wording: dynamic imports aren’t a statement, but a call, though not a function call.)