Skip to content
This repository has been archived by the owner on Jul 6, 2021. It is now read-only.

how to clean browser css cache #11

Closed
hank7444 opened this issue Feb 7, 2018 · 7 comments · Fixed by #228
Closed

how to clean browser css cache #11

hank7444 opened this issue Feb 7, 2018 · 7 comments · Fixed by #228

Comments

@hank7444
Copy link

hank7444 commented Feb 7, 2018

Hi @timneutkens

In production the stylesheet is compiled to .next/static/style.css. You have to include it into the page using either next/head or a custom _document.js. The file will be served from /_next/static/style.css

usually we will use hash code to clean browser cache if css file has any modification, like style.41d523.css

how can we do this on this plugin?

@Ehesp
Copy link

Ehesp commented Feb 19, 2018

I ended up doing the following in _document.js - this works for the time being, however we need to make sure the BUILD_ID is the same on a multi-deployment basis!

Note this builds from a src directory!

import Document, { Head, Main, NextScript } from 'next/document';
import { readFileSync } from 'fs';

let version = '';

if (process.env.NODE_ENV === 'production') {
  version = `?v=${readFileSync(`${process.cwd()}/src/.next/BUILD_ID`)}`;
}

export default class extends Document {
  render() {
    return (
      <html>
        <Head>
          <link rel="stylesheet" href={`/_next/static/style.css${version}`} />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    )
  }
}

@kachkaev
Copy link
Contributor

kachkaev commented Mar 5, 2018

It's also possible to use file's hash instead of BUILD_ID as version:

import { createHash } from "crypto";
import { readFileSync } from "fs";

// ...

let version = "";
if (process.env.NODE_ENV === "production") {
  const hash = createHash("sha256");
  hash.update(readFileSync(`${process.cwd()}/.next/static/style.css`));
  version = `?v=${hash.digest("hex").substr(0, 8)}`;
}

// ...

If the styles don't change between the builds, there's no need to download them and so everyone is better off.

In general, it's more efficient to calculate file cache using streams, but this should be unnecessary in our case. if .next/static/style.css is big enough to cause a hiccup during server setup, something is probably wrong 😃

crypto.createHash has been a part of Node's core nearly since the beginning of times, so there is no need to install a new module.


What are the caveats of this trick? If anyone sees them, could you please share?

@kachkaev
Copy link
Contributor

kachkaev commented Mar 6, 2018

Even though the workarounds in two previous comments solve the issue with style.css caching, all other static assets like images remain under the over-caching thread.

It'd be truly great to bring some webpack magic here! This would not only allow correct caching by turning paths into something like image.eld7hd4p.png, but would also make it possible to store images in folders with components. It's a bit of a shame that the images have to go to static in next.js, which separates them from a place where they really belong.

@kachkaev
Copy link
Contributor

kachkaev commented Mar 26, 2018

Here's one more discovery that might help those who have little CSS in style.css. That's the case when most of the styles come from styled-components or a similar framework. Turns out you can improve your website's performance by including the contents of .next/static/style.css directly into document, e.g. like this:

let style = null;
if (process.env.NODE_ENV === "production") {
  style = fs.readFileSync(`${process.cwd()}/.next/static/style.css`, "utf8");
}

// inside _document's Head:
{typeof style === "string" ? (
<style dangerouslySetInnerHTML={{ __html: style }} />
) : (
<link rel="stylesheet" href="/_next/static/style.css" />
)}

This helped me change the lighthouse performance score in production from 89 to 96 because First meaningful paint time decreased from 2.06 to 1.10 seconds. This is an advice in the original report that I followed:

screen shot 2018-03-26 at 11 31 08

When a stylesheet is heavy, it's cheaper to load it as a separate file and cache that request in the browser – this keeps the size of the server-rendered HTMLs small while avoiding extra round trips in consequent page requests. However, when style.css contains just normalize.css and a couple of simple global selectors, always keeping the entire stylesheet within the document's <head> and avoiding extra round trips appears to be a better thing. Obviously, caching is no longer a problem, because <style>{style}</style> is read from .next/static/style.css when your Next.js server starts.

Don't forget to import styles in a higher order component that works as a page wrapper:

# e.g. in hocs/page.js
import "./path/to/your/root/stylesheet.css"

Can this issue be considered as resolved?


If you want to check your website's performance, simply run

npm install --global lighthouse

lighthouse http://example.com/ --view

if ...style.css` screws up syntax highlighting in your editor, here's a trick:

let style = null;
if (process.env.NODE_ENV === "production") {
  // ${"css"} prevents editors from incorrectly highlighting code after css`
  style = readFileSync(`${process.cwd()}/.next/static/style.${"css"}`, "utf8");
}

@bryandowning
Copy link

bryandowning commented Apr 6, 2018

If you're going with the inlining approach, you may need to use:

<style dangerouslySetInnerHTML={{ __html: style }} />

Otherwise, things like quotes get converted to HTML entities.

@kachkaev
Copy link
Contributor

kachkaev commented Apr 6, 2018

Thank you @bryandowning, you are right! I updated the comment and removed <style>{{ style }}</style>.

@timneutkens
Copy link
Member

Will be possible when vercel/next.js#4119 lands for Next 6

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants