First of all, to find out what mincss
is read this blog post which explains what the heck this new Python tool is.
My personal website is an ideal candidate for using mincss
because it uses an un-customized Bootstrap CSS which weighs over 80Kb (minified) and on every page hit, the rendered HTML is served directly from memcache so dynamic slowness is not a problem. With that, what I can do is run mincss
just before the rendered (from Django) output HTML is stored in memcache. Also, what I can do is take ALL inline style
blocks and all link
tags and combine them into one big inline style
block. That means that I can reduce any additional HTTP connections needed down to zero! Remember, "Minimize HTTP Requests" is the number one web performance optimization rule.
To get a preview of that, compare https://www.peterbe.com/about with https://www.peterbe.com/about3. Visually no difference. But view the source :)
Voila! One HTTP request less and 74Kb less!
Now, as if that wasn't good enough, let's now take into account that the browser won't start rendering the page until the HTML and ALL CSS is "downloaded" and parsed. Without further ado, let's look at how much faster this is now:
Before:
report
After:
report
How cool is that! The "Start Render" event is fired after 0.4 seconds instead of 2 seconds!
Note how the "Content Download" isn't really changing. That's because no matter what the CSS is, there's still a tonne of images yet to download.
That example page is interesting too because it contains a piece of Javascript that is fired on the window.onload
that creates little permalink links into the document and the CSS it needs is protected thanks to the /* no mincss */
trick as you can see here.
The code that actually implements mincss
here is still very rough and is going to need some more polishing up until I publish it further.
Anyway, I'm really pleased with the results. I'm going to tune the implementation a bit further and eventually apply this to all pages here on my blog. Yes, I understand that the CSS, if implemented as a link
, can be reused thanks to the browser's cache but visitors of my site rarely check out more than one page. In fact, the number of "pages per visit" on my blog is 1.17 according to Google Analytics. Even if this number was bigger I still think it would be a significant web performance boost.
UPDATE
Steve Souders points out a flaw in the test. See his full comment below. Basically, what appears to happen in the first report, IE8 downlads the file c98c3dfc8525.css
twice even though it returns as a 200 the first time. No wonder that delays the "Start Render" time.
So, I re-ran the test with Firefox instead (still from the US East coast):
Before:
report
After:
report
That still shows a performance boost from 1.4 seconds down to 0.6 seconds when run using Firefox.
Perhaps it's a bug in Webpagetest or perhaps it's simply how IE8 works. In a sense it "simulates" the advantages of reducing the dependency on extra HTTP requests.
Comments
Post your own commentVery cool! <3 it :)
There'd be a much bigger audience for it if it came as a script, rather than tied up in Django stuff. Well, at least then I could use it ;)
Have you tried it on other frameworks, or websites apart from bootstrap ones?
mincss has nothing to do with Django. Did you see this? http://www.peterbe.com/plog/mincss
I have, for example, not used it on my one-page-tonnes-of-javascript *app* http://aroundtheworldgame.com because it's just too much Javascript dependent.
Aha! No, I missed that link. Brilliant :)
The obvious feature to add is taking a list of pages, applying the script to each of them, and then aggregating the entire result so that you have a site-wide CSS file that can be cached by browsers. I might just fork this and give it a try.
It already exists. The API is built such that you feed it URLs till your done then call the `.process()` method to start the calculation. For example:
>>> from mincss.processor import Processor
>>> p = Processor()
>>> p.process_url('http://example.com/page1.html')
>>> p.process_url('http://example.com/page2.html')
>>> p.process()
However, if you do that, the `p.inlines` will be confusing since you won't know which URL it came from. There's a shortcut to when you just have 1 single URL to worry about and that's to use `.process()` directly:
>>> from mincss.processor import Processor
>>> p = Processor()
>>> p.process('http://example.com/page.html')
Or you can actually do this too:
>>> from mincss.processor import Processor
>>> p = Processor()
>>> p.process('http://example.com/page1.html', 'http://example.com/page2.html')
Messing around with doing one page at a time and using inline is more advanced and perhaps something just for those who want the absolutely fastest.
Hi, Peter. It's fun to read about how mincss is making sites faster. I have one issue with your results, however. You mention how the page starts rendering much sooner. If we look at the "before" results ( http://www.webpagetest.org/result/130121_H_1MQ/1/details/ ) we see that rendering is blocked waiting for c98c3dfc8525.css. The weird thing is we can see that c98c3dfc8525.css is downloaded TWICE (see requests #2 & #12). It would be great if you could figure out why this happened (IE8 anomaly?) and re-run to get more comparable results.
It appears to respond with a 200 the first time so I honestly don't know what's going on.
I'll re-run the tests with a sane browser and see if it's reproduce-able.
Updated the blog post.
I can't get it to even process a page. The traceback is not very helpful.
>>> p.process('http://localhost:8001')
TROUBLEMAKER
u'a[href^="javascript'
TROUBLEMAKER
u'a[href^="javascript'
TROUBLEMAKER
u'a[href^="javascript'
TROUBLEMAKER
u'a[href^="javascript'
TROUBLEMAKER
u'a[href^="javascript'
TROUBLEMAKER
u'a[href^="javascript'
TROUBLEMAKER
u'a[href^="javascript'
TROUBLEMAKER
u'a[href^="javascript'
That's not a traceback. It's not an exception. It's just a print statement that the underlying CSS parser struggled with certain selectors.