简体   繁体   中英

React Server Side Rendering with Sass

As i have searched through the previous but could not find any relevant / solved issues on this so I ask...

Current stack: React, express/node, Sass, Webpack, Sass.

I have a question specific to the Styling of pages before server sends them to the browser.

Styling is working perfectly in the browser and by I mean when the pages are being navigated via browser router, all the styling works. However, I need to figure out a way to inject custom Sass styles to the page before the Server sends the page to the browser.

Current behavior: I access any page from the browser, I get serve the page with correct styling BUT there is a minor delay to get the correct styling in place to appear on the page. If view in slow motion, page appears without styling (because the server sent the page with no styling) and then browser takes over with correct styling.

I have tried webpack to generate all styles in a separate file but I can't seem to link this file to page before server sends it to the browser.

Any help / feedback will be appreciated?

server.js(route handler)

app.get('*', (req, res) => {
  const store = createStore();

  const promises = matchRoutes(Routes, req.path).map(({ route }) => {
    return route.loadData ? route.loadData(store) : null;
});

Promise.all(promises)
.then(() => {
  const context = {};
  const content = renderer(req, store, context);

  if (context.notFound) res.status(404);

  res.send(content);
 });
});

const PORT = process.env.PORT || 3000;

renderer.js

import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter, matchPath } from 'react-router-dom';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import serialize from 'serialize-javascript';
import Routes from '../client/Routes';

export default (req, store, context) => {

  // req.path is the url that the user is trying to access
  const content = renderToString(
  <Provider store={store}>
   <StaticRouter location={req.path} context={context}>
    <div>
      { renderRoutes(Routes) }
    </div>
   </StaticRouter>
 </Provider>
);

return `
  <html>
    <head>
      <link rel="stylesheet" href="../../public/styles.css">
    </head>
    <body>
      <div id='app'>${content}</div>
      <script>
        window.INITIAL_STATE = ${serialize(store.getState())}
      </script>
      <script src="bundle.js"></script>
    </body>
  </html>
`;
};

webpack.config.js (I omitted the rest of config since its not relevant)

const extractSass = new ExtractTextPlugin({
  filename: "styles.css",
  disable: process.env.NODE_ENV === "development"
});

module: {
rules: [{
    test: /\.scss$/,
    use: extractSass.extract({
        use: [{
            loader: "css-loader"
        }, {
            loader: "sass-loader"
        }],
        // use style-loader in development
        fallback: "style-loader"
    })
  }]
},
plugins: [ extractSass ],

This gives me a styles.css file in my /build folder. How can i link this to my html. If i try to throw a <link> tag the browser does not recognize the file.

I finally solved this issue by creating another route handler.

app.get('/styles.css', (req, res) => {
  res.sendFile(__dirname + "/" + "styles.css");
});

My webpack compiles a styles.css file which gets put in the /public folder. Please note i changed the path of my styles file in webpack to this: ../public/styles.css

app.use(express.static('public'));

Above statement ^^^ makes the styles.css file accessible and finally styles are applied by supplying the below <link> tag in the response of server to the browser.

<link rel="stylesheet" href="../../public/styles.css">

If anyone else have a better solution, I would love to get feedback.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM