Disclaimer.
The opinions in this presentation are my own. This is not a statement from the webpack team.
Problems with JavaScript bundlers:
You have to deal with problems you didn't have before.
Why isn't everyone just using ES modules?
No bare module specifiers:
No optional file extensions:
No package.json
resolving:
So, will we still need JavaScript bundlers in the future?
The following proposals are in very early stages. Take them with a grain of salt.
We don't need JS bundlers for that, it's already possible with ESM.
But: our current problem is resolving.
Things we can do: not rely on "cute" (Ryan Dahl) resolving features.
.mjs
extension for
JavaScript modules once ESMs land in Node.js.
.mjs
extension for
JavaScript modules once ESMs land in Node.js.
index.js
.
Advantages of BMS:
isomorphic-fetch
.
There is an intent to implement from the Blink team.
As a replacement for HTML imports:
There is an intent to implement from the Edge team.
CSS modules export a Constructable Stylesheet:
The goal: Turn this imperative API...
...into a declarative API:
Challenge: Allow cyclic dependencies between non-JS modules.
Source: Lin Clark's presentation about WebAssembly ES module integration.Cyclic dependencies between two WASM modules probably won't work at all.
Source: Lin Clark's presentation about WebAssembly ES module integration.Interesting proposal: Asset references
Another interesting proposal: import as-proposal
Advantages of these proposals:
#0CJS
(zero config).
One thing to note:
These are proposals to do the resource transformation on run time.
In order to get it fast for production, you would probably want to move this transformation to build time.
Hence, the transformation should be statically analyzable.
This is kind of hard to standardize because bundlers, Node.js and browsers have very different requirements.
But there is an interesting alternative to loaders...
Babel macros is a Babel plugin that allows you to evaluate code on build time.
How can this substitute loaders?
A good example of why modules should be able to configure their own loaders.
But how would that work without Babel?
With a package switch:
Current problem of Babel macros:
They require synchronous I/O on build and on run time.
Authors will slowly adopt the common denominator:
Bundlers won't be necessary because of compatibility reasons.
My hope: Platform independent APIs will converge some day.
Good example: the URL
constructor.
Bad example: fetch()
.
Will be less relevant with evergreen browsers and incremental language updates, but probably still necessary for production builds.
Also polyfills still require a lot of manual configuration.
Push is a new HTTP feature that allows the server to push resources pro-actively to the client.
Photo by Wolfgang Hasselmann on UnsplashCurrent problems with ESM and H2 push:
H2 push is still hard.
As implementations mature, we need to re-evaluate the question whether we should create chunks or not.
Let's assume that we really, really wanted to use H2 push.
How would it work?
There are two possible solutions:
Creating a dependency graph for H2 push is not the only performance optimization a bundler can do.
But the problem is: Getting the best performance requires a lot of configuration.
The solution: Give more data to the bundler so that it can perform automated decisions.
It could add resource hints automatically, like
preconnect
for all hosts that have been parsed
from the source code.
Example: webpackPrefetch: true
Another idea: webpackPreload: true
for render critical fonts.
But these manual hints can get outdated over time.
The idea:
Example: Create optimized subsets of fonts based on the actual usage of glyphs.
Another example: Inline render critical CSS.
critical
Running automated critical render path tests works especially well with the JAMstack.
But these automated tests are also error prone because we have to select popular routes.
What if instead of artifical usage data we used...
Data-driven user experiences: When real usage data drives the decisions about how to optimize the app.
And this is already possible today...
Guess.js...
prefetch
next.
navigator.connection.effectiveType
to avoid over-fetching and thus wasting expensive data on mobile.
Calculating content hashes and updating URLs will always require a bundler.
But there's another problem: In order to avoid propagating content hashes, we need to use an out-of-band configuration.
Example:
A single change invalidates all content hashes.
Possible solution:
All these optimizations are still necessary:
This is the reason why you will always have a build step for high-performance websites.
Bundlers were invented to use Node.js modules in the web, but this has changed.
Today, we use bundlers for a lot of reasons, like dev experience, compatibility and optimizations.
Yes, because bundlers can do stuff on build time that would otherwise need to be done on run time.
But the scope of bundlers will probably change.
My vision:
In order to make that happen without configuration overhead, we need to give bundlers more insights into our application...
Dev experience | Compatibility | Optimization |
---|---|---|
JS modules
|
Unified module systems
|
Optimized resource loading
|
HTML, CSS & WASM modules
|
Unified platform
|
Long-term caching
|
Custom languages
|
New language features
|
Smaller file sizes
|
Short feedback loop
|