Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webpack SCSS "Flicker" before pageload

I am working on an isomorphic React+Flux+express app and using webpack loaders for my sass (with sass-loader) and jsx files. I am not sure how to inject my stylesheets into a server side template. I took a look at the Extract Text Plugin for this purpose, but I really want to be able to use hot module replacement. Right now, I am loading my main.scss file in a React component like this:

if (typeof window !== 'undefined') {
  require("!style!css!sass!../../../styles/main.scss");
}

This works well for loading an individual stylesheet in a component, but there is a flicker before the React part is mounted. I understand that this is because this is injecting the stylesheet after my client-side app has loaded, so the stylesheet is not immediately available. This leads to the actual question: is there a way to inject this style-sheet into my server-side template while still using the webpack loader, or does this call for a separate gulpfile or express middleware? I was previously using a gulpfile to build my stylesheets, but I will end up having a lot of stylesheets and don't want them all jammed into one file.

like image 825
johnnyutah Avatar asked Feb 17 '15 19:02

johnnyutah


2 Answers

So the idea here is to have webpack compile with two separate configurations, one targeted for web (the browser), the other targeted for node (server-side). The server-side bundle can be required in other node/express code to build the pre-rendered HTML with the css.

There's a full example here, and I'll walk you through the relevant parts of it. https://github.com/webpack/react-starter

The prerender.html in app is the server-side template the author is using. Notice the following two lines of code:

<link rel="stylesheet" href="STYLE_URL">
<script src="SCRIPT_URL"></script>

See the config for webpack here https://github.com/webpack/react-starter/blob/master/make-webpack-config.js. The options being passed into here depends on whether you're doing a prod build or a dev build. Since we want to build the client bundle and the prerendering server bundle, let's take a look at https://github.com/webpack/react-starter/blob/master/webpack-production.config.js. It's creating two bundles, specifically the first one with separate stylesheets targetted for browser, and the second config is for prerendering.

For the first compilation, it uses this:

plugins.push(new ExtractTextPlugin("[name].css" + (options.longTermCaching ? "?[contenthash]" : "")));

to create a separate css file alongside your bundle. During the second compilation (for prerendering), it uses null-loader to load the styles (because we already have the styles we needed in a css file, we can just use that).

Now here's where we inject the path to css into your server template. Take a look here at a simplified server.js: https://github.com/webpack/react-starter/blob/8e6971d8fc9d18eeef7818bd6e9be45f6b8643e6/lib/server.js

var STYLE_URL = "main.css?" + stats.hash;
var SCRIPT_URL = [].concat(stats.assetsByChunkName.main)[0];
app.get("/*", function(req, res) {
    res.contentType = "text/html; charset=utf8";
    res.end(prerenderApplication(SCRIPT_URL, STYLE_URL, COMMONS_URL));
});

Assuming your output path for your bundle is the same as server.js (otherwise you can get the publicPath using require("../build/stats.json").publicPath and prepend it to your STYLE_URL and SCRIPT_URL above.

Then in your prerender.jsx: https://github.com/webpack/react-starter/blob/8e6971d8fc9d18eeef7818bd6e9be45f6b8643e6/config/prerender.jsx Grab your server-side template prerender.html and replace the URLs:

var html = require("../app/prerender.html");
module.exports = function(scriptUrl, styleUrl, commonsUrl) {
    var application = React.renderComponentToString(<Application />);
    return html.replace("STYLE_URL", styleUrl).replace("SCRIPT_URL", scriptUrl).replace("COMMONS_URL", commonsUrl).replace("CONTENT", application);
};

I admit this can be complicated and confusing, and if it's easier to use a separate gulpfile go for it. But do play around with this. If you need more clarification and help, you can post a comment and I'll get to it as soon as I can or you can use the webpack chatroom here (https://gitter.im/webpack/webpack), I'm sure one of the developers there can probably give you a better explanation than I did.

Hope this is somewhat(?) helpful!

like image 103
trekforever Avatar answered Nov 06 '22 07:11

trekforever


The initial flash appears because "style-loader" hasn't yet downloaded your CSS styles.

To resolve this issue, isomorphic (universal) rendering is required (markup generation on server, and not just in production mode - in development mode too).

You can achieve isomorphic rendering either by following the "react-starter" project path (mentioned in the comment above), or by using webpack-isomorphic-tools

https://github.com/halt-hammerzeit/webpack-isomorphic-tools

like image 1
catamphetamine Avatar answered Nov 06 '22 07:11

catamphetamine