-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
Poor performance as part of a webpack PostCSS build system #2544
Comments
Can you create a GitHub repo that we can use to test and benchmark? Setting it up myself means I'm just gonna put this off for months and months unfortunately, hah. |
Here ya go, all packaged up in a Docker container: https://github.com/nystudio107/tailwind-css-performance |
I did a little more research on this tonight, and removing Tailwind CSS entirely from the project resulted in instantaneous HMR'd CSS. Narrowing it down further, my @import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities'; Removing If we're just doing a raw import of a pre-generated file here, I'm guessing that it's just due to the massive side of the CSS file, and there may not be room for optimizations here. Going on this, and some other various issues filed here, I refactored it to split off the import '../css/tailwind-before.pcss';
import '../css/app-before.pcss';
import '../css/tailwind-after.pcss';
import '../css/app-after.pcss';
/**
* This injects Tailwind's base styles, which is a combination of
* Normalize.css and some additional base styles.
*/
@tailwind base;
/**
* This injects any component classes registered by plugins.
*
*/
@tailwind components;
/**
* Here we add custom component classes; stuff we want loaded
* *before* the utilities so that the utilities can still
* override them.
*
*/
@import './components/global.pcss';
@import './components/typography.pcss';
@import './components/webfonts.pcss';
/**
* This injects all of Tailwind's utility classes, generated based on your
* config file.
*
*/
@tailwind utilities;
/**
* Include styles for individual pages
*
*/
@import './pages/homepage.pcss';
/**
* Include vendor css.
*
*/
@import 'vendor.pcss'; So all we've done here is taken one large This results in near instantaneous rebuilds for any of my All of the CSS is still imported in the right order, and they all also build into one combined CSS file for a production build. I'll likely write this up for others who may run into it. |
Wrote the article up in: |
@adamwathan this could be nice to be added in the documentation Also props Andrew! This will help tons of people. |
I am surprised splitting up the files makes a difference — to me that points to the build process not doing what it should because it means each file is being processed independently which can have surprising side effects in your CSS. The CSS should be fully concatenated into a single file before being processed by PostCSS for everything to work properly. Otherwise you can't use If someone can put together a super minimal simple example that demonstrates the issue that would be helpful, the provided project with the Docker container looks super complicated. Tailwind builds everything from scratch in < 4s for me so I wonder if something in the configuration is causing Tailwind to run multiple times. |
@adamwathan did you see the part about the I haven't tried taking I realize that we can't As for the bare minimum repo, I'll have to leave that for someone else, or me when I have more time to devote to this. |
Ahh you are doing JS imports for each file rather than PostCSS imports, that makes sense that it would speed things up in that way but comes with the consequence I mentioned of your files being processed in isolation from each other. |
mmm yeah I removed the module.exports = {
plugins: [
require('tailwindcss')('./tailwind.config.js'),
]
}; ...and reduced by body {
background-color: blue;
}
@tailwind base;
@tailwind components;
@tailwind utilities; ...and the rebuild times are still as slow as mentioned previously, so it's either Tailwind-related or build system-related (or more likely a mix of both). |
Can only confirm that we're having similar issues. Our build itself is pretty quick but HMR with css being imported through js is very slow. So much it sometimes crashes Chrome 😅 |
If the floor here is When using HMR, especially iteratively, this can slow down the developer experience significantly. I don't know enough about Tailwind's internals, but perhaps something like a static cache that just returns the utilities rather than regenerating them if the config hasn’t changed would be ideal. Or perhaps other shortcuts/caching layers could be implemented if Other moonshots: rewrite the core in Rust or the like and bundle it up via WASM. Yeah, I know, it'd need PostCSS and a whole bunch of other things to be also similarly ported over... just seeing the gains from esbuild, and figured I'd put it out there. |
Yep something we can continue to work on when we’re able to prioritize it. In the mean time I’m 100% open to pull requests that improve performance if you have any ideas you’d like to try 👍🏻 |
I hear ya @adamwathan -- I think to do this effectively, the person would need to know Tailwind's internals really well, which unfortunately I'm not able to delve into currently. So I'll use my hack for now, until it can get addressed in core. Thx! |
For what it's worth I definitely see much faster rebuilds during a watch process than on initial build at least. This is from a simple Under 1s which given how infrequently you typically update CSS with Tailwind feels adequate to me. Would be interesting to see some flame charts of the webpack setup where CSS is imported directly into JS to see if the slowness is within Tailwind or perhaps the result of webpack doing something expensive with such a large bundle of CSS. Would be nice to speed it up either way but we will have less power to do so if it's just that "importing a big CSS file via webpack is slow". |
Man, I wish I was seeing build times anything close to that. Good idea on swapping in a generic import, I'm going to try that now. Will report back. |
I ended up using a somewhat middleground solution. Using @khalwat 's seperation into seperate tailwindcss files that I import in my javascript entry point, but stil using the Got my HMR time to this with the setup:
Updating a property in the tailwind config file gives me this:
Since we're not updating the tailwind config that much, this seems like a workable solution |
So @adamwathan it turns out you're 100% right on this, I did some timings, and there was effectively no difference between:
TL;DR: we can cut our build process time in half by not generating CSS sourcemaps, and by setting The separated builds described in the article I still think are useful, because it get us down to The details: #1
..... #2
..... #3 Inlined
..... So then I looked and noticed how positively massive the generated hot updates were... 8.77M for #1 Turns out almost all of this is the sourcemaps, which I had set to This is why the #2 Here are some timing tests:
....
|
build time is really slow even with hugo , which under the hood uses esbuild as its compiler which is the fastest compiler... |
Going to close this now because the underlying issue is that webpack performs poorly with large CSS files and there's just nothing we can do about that. Making Tailwind smaller by default is not really an option as it would severely cripple the usefulness of the framework. This would be better as an issue on the webpack repo around how to improve performance around the handling of large CSS files. Not trying to pass the buck but literally nothing we can do about it other than dedicate resources to making improvements directly to webpack which is not out of the question, but still work that would happen in their project and not in ours. |
Yep, agreed re: closing the issue. The only thing I'd add is that I think it's a common problem, so if generating less CSS is out of the question, perhaps a direction Tailwind could take is some kind of official way to handle the type of "CSS-splitting" mentioned in the article. I realize Tailwind is intended to work as one glob that's parsed by PostCSS, but perhaps some kind of official documentation on the problems people can run into re: performance, and the ways around it. |
I got the full Tailwind CSS build -- no CSS splitting, so global
|
@jan-dh I'm not sure how adding I've been able to port a number or projects to use the technique described in my article without ill effect, and with I'm sure there's something you miss out on but splitting it this way, but it seems to be working as expected for me. ¯_(ツ)_/¯ |
Just an update here for anyone who might be reading through this issue in the future.
In addition, the generation and HMR of Tailwind CSS has gotten slower in 2.x as well, but that may be just due to the fact that it's generating more CSS now. So what @adamwathan mentioned about losing global ref: #2820 |
This thread is a summary of my last 10h. Thank you for your great work @khalwat. This issue kills the developer experience and jumping to Tailwind 2.0 made it worse. The utilities +postcss precss processing = 15s on a relatively small project. |
No offense but I'm not sure how anyone working on a remotely real-life sized project is able to deal with the compile times. Compilation takes at least 10 seconds on my system (Ryzen 3900XT), spitting out a huge style chunk:
I've checked and 27 MB of the 31 MB styles.js chunk appears to be source-maps. |
@oliverw check my comment here: #2820 (comment) for how to eliminate the |
Kinda agreed...it's too slow as of v2. Have to wait for 10-15 seconds to see the changes. |
@rightaway have you split up your TW files? There is a comment above with an article to speeding up your TW builds. |
Yep, the hot reload page refreshes take their sweet time. Splitting up the CSS files does help though with build time at the very least. |
Have you tried following approach:
? More context: sveltejs/svelte-preprocess#275 (comment) For those who don't want to use You can also make a small loader, which will rebuild I would much prefer HMR in <1s with the need to reload page on @adamwathan what do you think ? Is there any problems that could arise from this approach ? |
I noticed slow build times when trying out tailwind 2.0 (with all the latest packages installed via yarn v2) I was able to get some speed ups by enabling the file cache under webpack (for webpack 5)
Bearing in mind sometimes if the cache gets corrupt you need to delete .yarn.cache\webpack (using yarn v2 pnp) |
This is a terrible issue for larger projects. If I enable dark mode, starting the dev server straight up crashes due to being out of memory, its completely unusable. |
Another solution I put here and that was to avoid using style-loader and try MiniCssExtractPlugin instead |
Splitting the Tailwind utilities etc worked well enough for me - custom CSS in another file entirely. Cut the build time from about 10-15s to about 2, which is fast enough in my books for now. Works well enough so can use Storybook 6 and not really feel any pain. Great work and great article, @khalwat , many thanks! |
The latest JIT option in version 2.1 solves this issue for me. https://tailwindcss.com/docs/just-in-time-mode |
@nzozor I'm using webpack and postcss, how did you get JIT working with webpack? It was breaking more than doing anything good for me. |
@dingman I'm using Angular and ngx-tailwind schematics which hides the webpack and postcss config. So I just updated tailwind.config.js in my case |
对于无法升级到最新版本tailwind的可以通过webpack插件解决这个问题: |
Using the latest Tailwind CSS (1.8.13 as of this writing), I'm seeing slowness very similar to #1620 & also #443, the performance of using HMR with
webpack-dev-server
and webpack 4 or 5 is quite slow.It takes about 10 seconds on my MacBook Pro 2019 just changing a single color in a
.pcss
file, and it appears externally that it's rebuilding everything each time. The building of Tailwind CSS seems to have gotten slower and slower as the amount of utilities it includes have gone up.I'm not sure what the caching implemented in 1.7.2 does, but in a long running process (and maybe it's already doing this but) what if all of the Tailwind-specific imports like:
...were cached in a long running process, so it just returns the pre-generated blob? I'd imagine you're probably already doing this, but have any instrumentation or profiling been hooked up to the build to determine where the bottlenecks are?
My postcss.config.js looks like this:
...and the whole setup is essentially what's here: https://nystudio107.com/blog/an-annotated-webpack-4-config-for-frontend-web-development#tailwind-css-post-css-config
It's not doing anything fancy re: the PostCSS part of the build, but its extremely slow compared to the HRM of JavaScript modules, etc.
I tried removing
postcss-preset-env
to see if it made a difference, but it doesn't seem to.Related: https://stackoverflow.com/questions/63718438/webpack-dev-server-slow-compile-on-css-change-with-tailwind
The text was updated successfully, but these errors were encountered: