vitejs / vite

Next generation frontend tooling. It's fast!
http://vite.dev
MIT License
67.82k stars 6.1k forks source link

Consider configurable bundler? #1835

Closed jods4 closed 3 years ago

jods4 commented 3 years ago

Is your feature request related to a problem? Please describe. Vite uses Rollup for the production bundling step. Sadly this is a bit of a deal breaker at the moment for me because of code-splitting. Rollup is historically made to bundle libraries and added code splitting only recently. Its support is limited in functionality (look at all issues asking for plugins that could influencing chunking) and Rollup core design makes it unlikely to change.

In more details: Rollup produces true ESM modules that evaluate immediately when loaded. So it is a requirement that every piece of code only appears in exactly one chunk (this is described as a Rollup principle many times in its issues). In turn, it means that each combination of chunk importing some code must result in a new chunk for that specific combination. That produces many chunks, in theory exponentially so.

I'm thinking of porting a large Webpack app to Vite because I like the development speed benefits. This app has between 200-300 routes/pages, which would produce around 400 chunks... Rollup is great for libs but such applications aren't a good fit for it.

Describe the solution you'd like Webpack has a lot of tools and flexibility to control chunking/bundling. It was designed to bundle code-split apps from the start and is battle-tested.

I think Snowpack had a great idea in the way they handle bundling: they still compile all modules like in dev, so all loaders, resolver configs, etc. apply equally. Then they push the "ESM app", which is entirely made of standards HTML/JS/CSS to a post-compilation "bundling+minifying" step. This step can use any bundler you like (they propose esbuild by default and have a plugin for Webpack) and requires minimal to no configuration as there's no compilation involved anymore, just bundling.

I would love if Vite followed that approach and made the final bundler a pluggable step, possibly using Webpack, after everything else was compiled as in dev.

Describe alternatives you've considered

yyx990803 commented 3 years ago

FYI you can manually chunk in Rollup via the manualChunks option.

Also, what is the problem with "many chunks"? You just describe it as a problem but I don't see why that is a problem. Vite automatically optimizes async chunk loading so no matter how many chunks it get split into, they are always fetched in parallel when a dynamic entry is fetched.

IMO Rollup's chunking behavior is more semantically correct.

Webpack has a lot of tools and flexibility to control chunking/bundling. It was designed to bundle code-split apps from the start and is battle-tested.

It's also slow, bloated, has extremely complex internals that are difficult to maintain plugins for. You are asking for a maintenance burden that I have no interest in committing to.

Using Vite for dev and bundling with Webpack: terrible idea because I wouldn't be able to share loaders, plugins, aliases config, etc.

Vite can probably introduce an "unbundled" output format which is technically not difficult to do, but that's not really a priority for now.

jods4 commented 3 years ago

FYI you can manually chunk in Rollup via the manualChunks option.

Yes, I'm aware. That's ok to configure a few "vendor" chunks or using pattern-based cases such as localizations. I'm not gonna manually sort out every 250 pages in my app and have to modify the build configuration every time I add a new page to my app. Not to mention that it's not transitive (so every page dependency will likely escape in their own chunk), and there's no checking for typos.

Also, what is the problem with "many chunks"? You just describe it as a problem but I don't see why that is a problem.

That's a loaded question, esp. considering you say in the very next sentence that you have added optimisations in Vite itself to mitigate the excessive splitting.

First: it's assuming you even use http/2. I just checked our app right now and even though everything seems to be configured so that it's HTTP/2 for some reason which I'll have to investigate, it doesn't. Without HTTP/2 there's no question you need to bundle more aggressively.

http/2 vastly improves, but the performance is still better with a reasonable bundle size. I don't want to make this overly long and I'm sure you're aware of compression, Chrome lack of speculative loading of import (which your async optimization counters), etc. These guys made for example a real benchmark and with 1000 bundles and short latency they've seen 3x worse perf compared to 10 bundles. https://medium.com/@asyncmax/the-right-way-to-bundle-your-assets-for-faster-sites-over-http-2-437c37efe3ff

Other aspect is that smart bundling doubles as prefetching. If I know a set of pages are in the same "domain" and are likely to be used together, I might as well deliver them as one. That'll be more reactive when my user clicks that link and the app doesn't need to do a server round-trip.

Finally: just from an operational perspective, I'd rather have 30 files, for most of them with names that indicate the "domain" they contain rather than 400 "chunk.[hash].js". Because one day I'll have to do some sad debugging, that's inevitable.

You are asking for a maintenance burden that I have no interest in committing to.

I have created very complex plugins that depend on webpack internals and I can't disagree with you here.

Snowpack approach is not that complex though, as webpack only performs the bundling of already transformed files. Their code fits in 400 LoC in a single file: https://github.com/snowpackjs/snowpack/tree/51d1f4b2638ca190459efee8bf21a1a5e209a2dd/plugins/plugin-webpack

If you make a plugin contract for bundling, similar to Snowpack, anyone could create this (maybe even reuse Snowpack's), it doesn't mean you would have to.

jods4 commented 3 years ago

Bonus: I might have found out why HTTP/2 is disabled. Apparently, IIS 10 fallbacks to https/1.1 if Windows Authentication is enabled.

This is a big enterprise app and we use Win. Auth to automatically login users. So no http/2 for us, which means we need tight bundles.

yyx990803 commented 3 years ago

You can work on a PR to support unbundled output for Vite if that is really important to you. It's a low priority on my side though. The fact that you can't have HTTP/2 is... very specifically your problem.

I'm not gonna manually sort out every 250 pages in my app and have to modify the build configuration every time I add a new page to my app.

Hint: I'm not gonna do huge new feature development just because it fits your very specific use case. I would suggest always consider what you can do to help the features you want land instead of demanding it like an enterprise customer.

jods4 commented 3 years ago

The fact that you can't have HTTP/2 is... very specifically your problem.

Sure, although I'd be surprised if I was the only one in this situation. Many enterprise apps use Win.Auth. Some are even less lucky than I and run pre-Win 10 servers. Although they should, not everyone deploys with HTTPS, so it's up to you to decide if the potential users are worth the hassle or not.

Hint: I'm not gonna do huge new feature development just because it fits your very specific use case. I would suggest always consider what you can do to help the features you want land instead of demanding it like an enterprise customer.

Don't take it like that, I assure you I'm not mean! I'm not demanding anything. If you think it's a silly idea, don't do it. At this point I'm not even using Vite, just considering options, so no worry.

I'm sure you noticed I spend a lot of time opening bug reports to both Vue and Vite and I think I do quite a bit to help the features I want to land. Recently I even published a prototype of auto.ref for Vite. I would love to do more but there's only so much time in a day and I have a full time job, so please accept my modest contribution.

❤️

jods4 commented 3 years ago

I'm not gonna manually sort out every 250 pages in my app and have to modify the build configuration every time I add a new page to my app.

In case this sounded bad: I didn't mean it in the sense of "I don't want to do any effort", rather "I don't think that's the proper way to handle this"

yyx990803 commented 3 years ago

@jods4 thanks, me and the team do appreciate your feedback in the RFCs and I do believe you have helped shape the Vue 3 API for the better.

However, I'd also appreciate a less combative/demanding tone when you are communicating w/ OSS maintainers. (by "combative" I don't mean offensive language, but that you often focus too much on proving that you are right.) I know you probably don't mean it, but you do often come off as being such and it has been brought up by a few other members on the team as well (and some of them don't know you as well as I do so they probably get more annoyed than they should). I think you'd be able to help improve the Vue ecosystem even more if you take a more modest approach when voicing your opinions.

jods4 commented 3 years ago

@yyx990803 That is very constructive and I appreciate you told me. I'm actually a bit shocked that other team members brought it up. Makes me feel special... in a bad way.

English is not my first language and communication through text only makes things worse, compared to a real discussion.

Please be assured that I'm far less demanding than I sound and certainly not combative or offensive. Take everything I write lightly, it's never intended to be otherwise. I'm rather nice in real life 😉

I'm not sure how to change my prose but I'll try.

yyx990803 commented 3 years ago

Closing for now, maybe we will reconsider this in the future if more use cases arise.

jods4 commented 3 years ago

I agree, the more I think about it and look at the code, the more it seems complicated. I'm brainstorming several ideas, as I love the fast dev experience of O(1) bundlers, but "ESM bundles" don't really cut it in production for some projects. I hope to find some middle-ground, although sticking to Webpack is also an option.

Venryx commented 3 years ago

You can work on a PR to support unbundled output for Vite if that is really important to you.

I'm potentially interested in working on adding support for unbundled output for Vite.

Are you still open to a PR for this functionality in Vite itself? Or would it need to be as a plugin?

Shinigami92 commented 3 years ago

@Venryx In principle, we are open to any PRs. We can always consider afterwards whether these will be merged or not. Either way, you get a deeper insight into the code of Vite and can always try to create a plugin in the case the PR would be rejected.

Venryx commented 3 years ago

A very quick first attempt:

import fs from "fs";
import path from "path";

// [rest of config...]

config.plugins.push({
  name: "vite-plugin-esmodules-output",
  enforce: "post",
  transform(src, id) {
    if (id == "/@react-refresh") return undefined;

    const destPath = id.split("?")[0].replace("src", "esm").replace("node_modules/.vite", "esm/node_modules");
    const destFolder = path.dirname(destPath);
    fs.mkdirSync(destFolder, {recursive: true});
    fs.writeFileSync(destPath, src);
  }
});

It outputs files; however, the contents of those files are not sufficiently transformed. For example, the imports: import React from "react"; is left as is rather than converted into import React from "./esm/node_modules/react.js";.

So I'd need to either find a way to access the "final" transformed version that Vite produces for the dev-server somehow (maybe through monkey-patching of Vite's internals, if that's possible), or duplicate the import-transformation logic within the plugin.

May come back to this. (I'm in the process of comparing between Vite and Snowpack, so depends on the when+what of my eventual choice.)