New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EPIC]Consider generating multiple bundles in favor of modern browsers #2114

Open
kripod opened this Issue Sep 14, 2017 · 16 comments

Comments

Projects
10 participants
@kripod
Contributor

kripod commented Sep 14, 2017

According to this article, modern browsers may profit from bundles loaded with support for the ES2015 module syntax. Using the methods described in the article may also reduce server load because of the reduced bundle sizes.

@KyleAMathews

This comment has been minimized.

Contributor

KyleAMathews commented Sep 14, 2017

Huh super interesting! Would love to see someone set this up on gatsby! Should be doable as a plugin.

Btw, the main benefit appears to be that you don't have to polyfill for newer browsers so the code loaded is significantly smaller.

@jquense

This comment has been minimized.

Collaborator

jquense commented Sep 14, 2017

That article seems confused (or I am)...the main jist is to not compile modules via babel. The problem with that tho is that you don't get a bundle out that has uncompiled es modules, you get a compiled webpack bundle with no modules. Babel won't compile the modules but webpack will. So you can use the "modern" babel config with any browser, in fact the v2 gatsby branch (which uses webpack2) already does this.

@jquense

This comment has been minimized.

Collaborator

jquense commented Sep 14, 2017

ahhhh I get it sorry i'm the dense one. The point is just that you can simplify feature detection by checking script module tags?

@KyleAMathews

This comment has been minimized.

Contributor

KyleAMathews commented Oct 3, 2017

@davidtheclark over at Batfish is working on a PR for a similar feature mapbox/batfish#188

This technique seems really promising.

We'd need to see how much time it adds to the build but if it's not that much more, I'd definitely consider turning it on by default. If the extra build time hurts enough, we could make it optional.

@robwierzbowski

This comment has been minimized.

robwierzbowski commented Mar 6, 2018

@jquense I'm really into this approach, and have been applying parts of it in prod code. To summarize my understanding:

  • Transpiled code is often larger and less performant at runtime than es6+ code.
  • script type="module" is a good "cut the mustard" line for a lot of es6+ features. Remember cut the mustard from maybe 5-6 years ago?
  • We should still bundle our modules
  • We can create a bundle with much less transpiling for modern browsers, and use script type="module" as a sneaky feature detect to load a minimally transpiled bundle on newer browsers, and a more transpiled / es5 bundle on older browsers.

@KyleAMathews KyleAMathews added this to To Do in Gatsby v2 Release via automation Mar 6, 2018

@KyleAMathews

This comment has been minimized.

Contributor

KyleAMathews commented Mar 6, 2018

Yup! And per our parallel polyfill discussion, the script type="module" would need pretty minimal polyfills and be very cheap to deliver them over CDN.

@davidtheclark

This comment has been minimized.

davidtheclark commented Mar 6, 2018

I ended up holding off on that Batfish PR but implemented the plan on another build system tool — with the idea of bringing it into Batfish after Babel 7 is released. It worked well overall, but there were two hiccups:

  • Minification becomes more complicated, because you want to minify your ES2015+ bundle without having to compile it to ES5. Uglify has been moving pretty fast to catch up to ES2015+, though, so this might not be much of an issue anymore. Seems to be working ok for me.
  • You have to run the Webpack build twice, which is kind of a bummer.
@robwierzbowski

This comment has been minimized.

robwierzbowski commented Mar 6, 2018

Yeah, the build twice is a pain. Webpack persistant caching might hep that, depending on how it’s implemented.

Babel-minify worked well for me too. Not quite as small as uglify es branch, but not as many edge cases either.

@mquandalle

This comment has been minimized.

Contributor

mquandalle commented Jun 6, 2018

For reference Meteor 1.7 introduced the ability to generate two different JavaScript bundles, one for legacy browsers that includes JS polyfills for features that aren't natively implemented by the browser, and one for modern browsers without these polyfills. The rationale for the Meteor design is exposed in meteor/meteor#9439 and https://blog.meteor.com/meteor-1-7-and-the-evergreen-dream-a8c1270b0901.

@KyleAMathews

This comment has been minimized.

Contributor

KyleAMathews commented Jun 7, 2018

Cool! Super nice to see Meteor is doing this. This is a very high priority to ship with v2.

@benjamn

This comment has been minimized.

benjamn commented Jun 8, 2018

@KyleAMathews I'd be happy to discuss how this might work in Gatsby! It's a nice selling point for Meteor right now, but I think it's safe to say Gatsby's users have different needs, and are not about to switch to Meteor.

Most of my thoughts are in the blog post that @mquandalle linked above, but here's a slightly shorter explanation that I recently typed in a FB comment:

Here's the gist: In previous versions of Meteor, we had only one set of static assets (JS, CSS, etc.), and those assets needed to work in any browser that we claimed to support.

In Meteor 1.7, these assets have become "legacy," which means they still work perfectly well in any browser that we support. Of course, in order to support older browsers, the JS bundle needed to be compiled using Babel, and the resulting code was larger, messier, less native.

In Meteor 1.7, we wanted to start shipping a smaller, less messy, more native bundle tailored for "modern" browsers that have native support for a growing set of ECMAScript features. To do that, we considered each polyfill and Babel plugin that we were previously using, and asked what would happen if we just stopped using that polyfill or plugin. Of course, removing a plugin that handles class syntax means your bundle can only run in browsers that natively support class syntax, so there's a cost to removing each plugin. Removing polyfills and plugins narrows down the set of browsers that can be considered "modern" (which just means it's safe to send them whatever modern bundle we built).

So that's essentially where the modern bundle comes from: it's a copy of the legacy bundle, with as many polyfills and plugins removed as we felt comfortable removing. For every feature that we stopped simulating in older browsers, we found the minimum version of each browser that natively supports that feature, so only those browser versions are eligible to receive the modern bundle. More precisely, to receive the modern bundle, a browser needs to meet the minimum version for all the features that we no longer compile.

You might think that removing, say, half of the plugins would result in a bundle that only a very small percentage of browsers could handle, but the amazing thing is that more than 80% of the world's browsers are "evergreen," and they all support basically the whole ECMAScript standard, so you have to remove a whole lot of plugins before you end up with a bundle that they can't handle. We'd be in a very different (worse) situation if every major browser supported some arbitrary partial set of ECMAScript features. Luckily, the evergreen browsers are in vigorous agreement.

Right now, the most bleeding-edge feature that we leave uncompiled in the modern bundle is probably async/await. Yes, that was just finalized in 2017, but all the evergreen browsers already support it. At some point we'll be able to stop compiling things like decorators and public/private class properties, because enough browsers will support those features natively, but we're not there yet.

Sniffing user agents can be dicey, but if the server is ever not 100% confident about the browser/version of a client, we can always fall back to the legacy bundle.

@benjamn

This comment has been minimized.

benjamn commented Jun 8, 2018

One quick question: does Gatsby have any mechanism for delivering different HTML to different browsers, or is the <script type="module">/<script nomodule> (for differentiating between JS bundles) the only trick a static site can use?

@m-allanson

This comment has been minimized.

Contributor

m-allanson commented Jun 8, 2018

Gatsby probably needs to rely on <script type="module">/<script nomodule>

@benjamn

This comment has been minimized.

benjamn commented Jun 8, 2018

I think that's a perfectly reasonable strategy, though I just finished tweeting some thoughts on the limits of that approach: https://twitter.com/benjamn/status/1005104014944538624

@KyleAMathews

This comment has been minimized.

Contributor

KyleAMathews commented Jun 13, 2018

Vue is shipping with this as an optional mode for builds https://cli.vuejs.org/guide/browser-compatibility.html#modern-mode

@alexparish

This comment has been minimized.

Contributor

alexparish commented Jun 17, 2018

This would be a fantastic addition for v2, but I understand there are many competing priorities for that release.

Out of interest does anyone know if the non-transpiled ES6 (eg. arrow functions, classes) executes as fast as transpiled code in modern browsers now?

@marisamorby marisamorby moved this from To Do - v2 blockers to non-blocking but would be great to have in Gatsby v2 Release Jul 27, 2018

@m-allanson m-allanson added the Epic label Aug 24, 2018

@m-allanson m-allanson changed the title from Consider generating multiple bundles in favor of modern browsers to [EPIC]Consider generating multiple bundles in favor of modern browsers Aug 24, 2018

@pieh pieh added the 🎯 OSS label Aug 24, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment