-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Tailwind 2.0 poor performance as part of a webpack PostCSS build system #2820
Comments
Can you explain specifically what you mean by this? Does the build fail with an error or something? I'm surprised anything is different here as there were basically no internal changes between 1.9 and 2.0. |
Ah yes, sorry, I pushed up a new branch that demonstrates the problem: https://github.com/nystudio107/tailwind-css-performance/tree/broken-tailwind-2.x So if I try to do this in global.pcss: .my-cool-class {
@apply pt-4 mb-2 text-lg
}
body {
background-color: orange;
@apply my-cool-class
} I end up with this:
The above works fine when using Tailwind 1.x, despite being broken up into separately imported files. I assumed this was a result of the various internal |
I made a branch that shows the same code working in Tailwind 1.x: https://github.com/nystudio107/tailwind-css-performance/tree/works-tailwind-1.x Here's the diff between the two branches: nystudio107/tailwind-css-performance@broken-tailwind-2.x...works-tailwind-1.x It's just changing the Tailwind CSS semver from It works with Tailwind CSS 1.x, and it breaks with Tailwind CSS 2.x, with the aforementioned error:
|
I'm not sure if it's related, I have a hunch it might be.. But upgradng a Sapper/Svelte (using postcss with rollup though, not webpack) project to Tailwind 2.0 has brought me a new headache. Sapper's compile time has 10-100x'd, and I can't make changes on the fly. I get "Killed 137" messages. Trying to debug now, I'll update this comment with any findings. Edit: I found that
|
I'm going to add to this. I just upgraded to tailwind 2.0 and I'm getting brief beach-balling on my browser whenever HMR happens in a svelte app on snowpacker. PostCSS plugins are just tailwind, postcss-nested, and autoprefixer. |
Autoprefixer was my problem, I think. Still investigating. I had to roll it back to an older version than what TW 2 uses. My dev experience and build times are back to “normal”. TW has |
We're also experiencing very slow build times with our setup. Here's another reproduction repo which I hope will help resolve the problem: https://github.com/Cherry/postcss-tailwind-slowdown In this stripped down repo, we have a very basic PostCSS workflow setup, with Other system info: OS: Windows 10.0.19041 Build 19041 If we can provide any more information to make debugging this easier, please let me know and I'd be happy to do so. |
Hi! I've tested your setup @Cherry and when I run Is it possible to migrate to postcss 8? I'll keep doing some research to see if I can find other culprits. |
Thanks so much for taking a look @RobinMalfait. Unfortunately we're not able to upgrade just yet. |
I can confirm, that the switch from tailwind 1.9.6 to 2.0.1 with postCSS8 in place increased our compilation time during development no less than 33% and sadly breaks the workaround by @khalwat Please let us know, if additional information can be helpful to classify or to debug the issue. |
I can confirm the same thing as what @khalwat mentioned, we reverted back to 1.9, I hope this issue gets some priority, as TW2.0 slow down our dev environment a lot, HMR takes much too long, and our compilation time ( after changing out postcss-preset-env , to leverage autoprefixer directly, and abandoning nested postcss ) still increases depending on machines by 35-55%, which brings in a rather complicated ( but necessary ) setup build times to ~2.8s. |
@gremo Could you also try a simple postcss build and check the speed of initial build and rebuild on file change without webpack being involved? Something like Btw, I have purge |
@ahtik sure PostCSS 8 + TailwindCSS 2:
Processing src\style.css... PostCSS 7 + TailwindCSS 1.9:
Processing src\style.css... Config is pretty the same in the two cases:
So what's wrong with Webpack 5 + postcss-loader? |
@gremo thanks! Could you also try with the For my setup, I've started to use a bit of unconventional method with the package.json commands (although using snowpack instead of webpack, but the idea is the same):
My tailwind.config.js:
|
PostCSS 8 + TailwindCSS 2:
A few changes show this: Processing src\style.css... Waiting for file changes... Waiting for file changes... Waiting for file changes... PostCSS 7 + TailwindCSS 1.9:
Processing src\style.css... Waiting for file changes... Waiting for file changes... Waiting for file changes... |
@gremo Look at the difference in the size of the bundles there... A large part of the issue with performance in a HMR scenario is just the sheer quantity of data that's having to be processed, wrapped in JS, injected into the DOM, and then the browser has to re-parse it all to rebuild the CSSOM. I'd recommend cutting down on the size as much as possible, by removing CSS maps and using a more performant & All of this aside, even fully optimized, the real issue is we need to be able to rebuild only what is has to be rebuilt. The way Tailwind CSS 2.x is currently built requires that everything is rebuilt every time. No optimizations in the world are going to make that performant in a HMR scenario through the entire pipeline, regardless of the tool used. And the amount of CSS being generated will only get larger as features are added. Some way to do "CSS splitting" so Tailwind CSS can rebuild only the necessary bits—but keep a global state to allow This worked with Tailwind CSS 1.x, so I'm hoping the folks at Tailwind Labs can figure out a way to restore the functionality as described in the article in my initial post. |
@khalwat this was a great suggestion in #2544. I'd try my hand at making a PR but am in the same boat of having zero familiarity with the Tailwind codebase. @adamwathan how much time/effort do you think it'd take one of us get up to speed enough to implement such a cache? FWIW I've just resigned myself to tearing out my custom classes with |
@lustrousgorilla you can also roll back to Tailwind 1.9.x -- because the CSS splitting mentioned in the OP here works with Tailwind 1.x, so you'll get super fast HMR times and global It just doesn't work with Tailwind 2.x, either due to internal changes in Tailwind CSS, or some other part of the package tree they use. Here's the article again: Speeding Up Tailwind CSS Builds |
Hey all! I'm looking further into this, and while Tailwind is currently not the fastest, I believe that there are other issues as well. (This has also been mentioned before, but wanted to clarify it anyway) github.com/tailwindlabs/webpack-tailwind on master [!?] is 📦 v1.0.0 via ⬢ v15.2.0
❯ webpack
config changed: 1
TAILWIND(): 8.353s
[webpack-cli] Compilation finished
asset bundle.js 17.2 MiB [emitted] [minimized] [big] (name: main)
runtime modules 657 bytes 3 modules
orphan modules 89 bytes [orphan] 1 module
cacheable modules 17.2 MiB
./src/index.js + 1 modules 159 bytes [built] [code generated]
./src/file.css 17.2 MiB [built] [code generated]
./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
bundle.js (17.2 MiB)
WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
main (17.2 MiB)
bundle.js
WARNING in webpack performance recommendations:
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
webpack 5.9.0 compiled with 3 warnings in 30110 ms I timed the actual time spent inside Tailwind, which results in Another funny observation is that when you run github.com/tailwindlabs/webpack-tailwind on master [!?] is 📦 v1.0.0 via ⬢ v15.2.0
❯ webpack --watch
[webpack-cli] Compilation starting...
config changed: 1
TAILWIND(): 8.004s
[webpack-cli] Compilation finished
asset bundle.js 17.2 MiB [compared for emit] [minimized] [big] (name: main)
runtime modules 657 bytes 3 modules
orphan modules 89 bytes [orphan] 1 module
cacheable modules 17.2 MiB
./src/index.js + 1 modules 159 bytes [built] [code generated]
./src/file.css 17.2 MiB [built] [code generated]
./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
bundle.js (17.2 MiB)
WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
main (17.2 MiB)
bundle.js
WARNING in webpack performance recommendations:
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
webpack 5.9.0 compiled with 3 warnings in 29644 ms
[webpack-cli] watching files for updates...
[webpack-cli] Compilation starting...
TAILWIND(): 3.262s
[webpack-cli] Compilation finished
asset bundle.js 17.2 MiB [emitted] [minimized] [big] (name: main)
runtime modules 657 bytes 3 modules
orphan modules 89 bytes [orphan] 1 module
cacheable modules 17.2 MiB
./src/index.js + 1 modules 159 bytes [built] [code generated]
./src/file.css 17.2 MiB [built] [code generated]
./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
bundle.js (17.2 MiB)
WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
main (17.2 MiB)
bundle.js
WARNING in webpack performance recommendations:
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
webpack 5.9.0 compiled with 3 warnings in 24589 ms
[webpack-cli] watching files for updates... What you will notice is that the config only changed once, and we already have some caching in place. This results in:
Next, I made a change inside in my css file where I have some [webpack-cli] Compilation starting...
TAILWIND(): 3.856s
[webpack-cli] Compilation finished
asset bundle.js 17.2 MiB [emitted] [minimized] [big] (name: main)
runtime modules 657 bytes 3 modules
orphan modules 89 bytes [orphan] 1 module
cacheable modules 17.2 MiB
./src/index.js + 1 modules 159 bytes [built] [code generated]
./src/file.css 17.2 MiB [built] [code generated]
./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
bundle.js (17.2 MiB)
WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
main (17.2 MiB)
bundle.js
WARNING in webpack performance recommendations:
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
webpack 5.9.0 compiled with 3 warnings in 26862 ms
[webpack-cli] watching files for updates... The time spent in tailwind itself is So, @lustrousgorilla implementing (more) caching in Tailwind itself won't fix any of these bigger problems. That said, I am going to look into why the proposed solution by @khalwat for 1.9 (#2820 (comment)) doesn't work anymore. |
@RobinMalfait specifically if you could look into the times it takes when you do:
...and then not on the initial build, but rather on rebuild times when you change something in one of your CSS files. That is the HMR timing that uses Thanks for looking into this! |
Well, I'm not an expert about bundlers and build systems, but here my two cents . Considering that:
... the problem has to be the build chain or the PostCSS loader of Webpack. |
@ahtik in my tests, purging in development only helped if you also didn't disable CSS sourcemaps... because then you're cutting out a good amount of file size with your purging. But it's better to disable CSS sourcemaps in local dev when using Tailwind CSS and a HMR system imo. |
@gremo we're talking specifically about the HMR time. In your Snowpack setup, if you start the dev server up, and make a change to your CSS, what is the resulting rebuild time that results from it rebuilding all of the CSS and injecting it dynamically into the DOM? I've seen some colleagues of mine working with Snowpack, and they had similarly slow times. Are we missing something? |
@khalwat I just learned snowpack yesterday sorry 😄 I can't figure out how to print stats about the time to rebuilt + inject. It seems way faster that my previously posted setup (Webpack 5, PostCSS 8, TW 2). About 3 secs I would say. |
Going to close this at this point since we are back to v1 behavior and there's nothing left we can do in this codebase to improve things. Any additional improvements will come in the form of improvements to css-loader or a new loader designed with large CSS files in mind. Glad we were at least able to get the old workaround working 👍🏻 |
Been heads-down on another project, but will verify shortly! |
https://nystudio107.com/blog/speeding-up-tailwind-css-builds tailwindlabs/tailwindcss#2820 Basically, the splits the CSS up into a bunch of separate files so that Webpack only has to rebuild the *whole* tailwind thing rarely. HMR reloading when the site is served is much faster!
Regarding the build of tailwind itself, is it incremental? Ideally it would build the whole thing initially, and then only apply partial updates, e.g. when the tailwind config file changes. |
In case it's helpful to anyone else, I'm seeing dramatically improved build times (30s to 15s) by including Tailwind via JS Posted details in #3321! |
In an Angular app using currently Still really slow. Specially as new components were added recently. The build take actually > 10 minutes. |
I found the issue while upgrading Previously, the following purge configuration was slowing the build:
The new default config provided by ngneat is the following and fixed the build time:
See this PR for the details. The build time went down to 2 min. |
woa, that so much much good. Thank you a lot @peterpeterparker |
One thing I've discovered is that extract-css-chunks-webpack-plugin I think is a quicker option than style-loader style-loader extract-css-chunks-webpack-plugin In the rules I'm now using
In the plugins
Another option is to try the webpack file cache
I'd be tempted to leave that off though as sometimes the cache needs to be manually deleted and it seems to slow things down a bit with extract css chunks Edit |
@grbd it's likely due to the sourcemaps -- disable them: nystudio107/tailwind-css-performance@259a998 ...and disable URL resolving since PostCSS will do that for us |
By disabling source maps Style LoaderInitial build: 18 vs 12 seconds ExtractCssChunksInitial build: 11 vs 8 seconds I'd imagine on a much larger site ExtractCssChunks may have a much bigger speed impact |
I think ExtractCssChunks hasn't been updated to work with webpack 5... Isn't ExtractCssChunks merged with MiniCssExtractPlugin? Also the cache settings you mention should only apply to production builds... by default, if |
Like I said I would ignore the file cache setting if using the css extraction plugins I think for extractcsschunks there may be issues with webpack 5 if setting it up to generate multiple css files I just had a quick go with mini-css-extract-plugin |
@grbd inspired by your timings, I did some test last night with MiniCssExtractPlugin but unfortunately I didn't see any speed gains. This is using webpack 5 -- I'm not sure what the differences in our setups might be, I was hoping I'd see similar results, but it wasn't any quicker than using |
I've put up an example here under clientjs\webpack\site.rules.js One thing I tend to do is split the webpack build into two parts, vendor and site
|
Man sorry this is about 2 months late @adamwathan & @RobinMalfait but I've verified the changes made, and everything works great with "CSS splitting" again (which I know you already know... just finally getting around to verifying it). Updated the article: Speeding Up Tailwind CSS Builds This technique is used in this real-world project for the devMode.fm website in case anyone wants to try out a live example. |
对于无法升级到最新版本tailwind的可以通过webpack插件解决这个问题: |
Description of the problem
Tailwind CSS 2.0 builds slowly as part of a HMR webpack build system. It is slower than the release, but that could just be due to the amount of CSS generated increasing.
I filed a very similar issue a month ago #2544 and worked around the problem by splitting the CSS up into separate chunks. Global
@apply
still worked, and everything was great.It's all written up in the article Speeding Up Tailwind CSS Builds
However with Tailwind CSS 2.0, the technique described in the article breaks.
Desired solution
I'm hoping to have the DX of working with Tailwind CSS 2.0 be improved from a HMR build time perspective.
I realize that you can only optimize your generation of CSS so much, and a PR is in the works to do just that with
esbuild
. I also realize that part of the slowness here is simply webpack and the surrounding ecosystem being slow when dealing with massive amounts of CSS (though I've optimized that quite a bit here).However, the paradigm that Tailwind CSS is using is what generates a massive amount of CSS, and webpack is a very widely used tool that many things are built upon.
Some way to address the DX here would be great; huge gains can be made using the CSS splitting technique described in the article, maybe there could be a way to restore that functionality to Tailwind 2.x?
Link to a minimal reproduction:
I made a new branch with Tailwind 2.0, webpack 5, PostCSS 8, and all the goodness:
https://github.com/nystudio107/tailwind-css-performance/tree/problem-tailwind-2.x
While
2.5s
isn't awful, this is in a pretty minimal CSS setup, where I was getting about1.2s
for the equivalent builds in Tailwind 1.x, and using the technique described in the article, the builds were around182ms
The text was updated successfully, but these errors were encountered: