React server rendering road 07 — adding CSS Style

Time:2020-11-21

All source code, documents and images are in GitHub’s repository. Click to enter the repository

Related reading

  • React server rendering road 01: project infrastructure construction
  • React server rendering road 02 — the simplest server rendering
  • React server rendering road 03 — routing
  • React server rendering road 04 — redux-01
  • React server rendering road 05 — redux-02
  • 06 the way to optimize service rendering
  • React server rendering road 07 — adding CSS Style
  • React server rendering road 08-404 and redirection
  • React server rendering road 09 — SEO optimization

1. Add CSS style

  • The webpack we configured before only configured JS, and there is no configuration for CSS and CSS preprocessor. Therefore, we need to configure CSS, and we use sass preprocessor uniformly

one point one webpack.client.js Configuration of

  • Now, why not configure CSS to webpack.base.js Because the server does not recognize the CSS code, we cannot simply write the CSS configuration information in the webpack.base.js in
  • Download dependencynpm i node-sass sass-loader -D
  • Modification webpack.client.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              modules: true,
              localIdentName: '[name]_[local]_[hash:base64:5]'
            }
          }
        ]
      }
    ]
  }
}

one point two webpack.server.js Configuration of

  • The server can not directly identify CSS resources, but we still need to be able to identify CSS resources on the server side, so we use a library, which is isomorphic style loader, to identify CSS for the server
module.exports = {
  module: {
    rules: [
      {
        test: /\.css?$/,
        use: ['isomorphic-style-loader', {
          loader: "css-loader",
          options: {
            importLoaders: 1,
            modules: true,
            localIdentName: '[name]_[local]_[hash:base64:5]'
          }
        }]
      }
    ]
  }
}

1.3 components use CSS Style

  • When components use CSS style, you can directly import CSS files as before, and apply styles to the corresponding DOM tags
  • /src/containers/Home/index.css
/**
 * /src/containers/Home/index.css
 */
.wrapper {
  background: orange;
}

.title {
  color: red;
  font-size: 26px;
}
  • /src/containers/Home/index.js
// /src/containers/Home/index.js
import styles from './index.css';

class Home extends Component {

  render() {
    return (
      <div className={styles.wrapper}>
        <h2 className={styles.title}>HELLO, HOME PAGE</h2>
      </div>
    );
  }
}
  • If we use this method directly, we can see the corresponding CSS style on the page
  • But there are two problems

    • The first problem is that the browser must enable JS. If JS is not enabled, the style will not take effect
    • The second problem is that when our page refresh rate is too fast, and do not use cache, then the page has a very obvious jitter
  • These two problems are very bad for users, so we further improve them

1.4 inject the style into the server-side HTML template

  • In fact, the way we used above is to write CSS in JS. If we look at the source code of the page, we can only find the class name of DOM element on the page, but we can’t find any CSS code, because all of them are in the/ client.js So we need to take CSS out of JS and write it on HTML page
  • When we introduce a CSS file, the module will bring some properties. These attributes are provided by webpack. We can take a look at them
import styles from './index.css';

console.log(styles);

{ wrapper: 'index_wrapper_2wP7c',
  title: 'index_title_39dQ8',
  _getContent: [Function],
  _getCss: [Function],
  _insertCss: [Function]
}

--------------------------------------------------

console.log(styles._getContent());

[
  [ './node_modules/[email protected]@css-loader/dist/cjs.js?!./src/containers/Home/index.css',
    '.index_wrapper_2wP7c {\r\n  background: orange;\r\n}\r\n\r\n.index_title_39dQ8 {\r\n  color: red;\
\n  font-size: 26px;\r\n}\r\n', '' ],
  toString: [Function: toString],
  i: [Function],
  locals: { wrapper: 'index_wrapper_2wP7c',
    title: 'index_title_39dQ8',
    _getContent: [Function],
    _getCss: [Function],
    _insertCss: [Function] } ]

--------------------------------------------------

console.log(styles._getCss());

.index_wrapper_2wP7c {
  background: orange;
}

.index_title_39dQ8 {
  color: red;
  font-size: 26px;
}
  • We output some properties of styles in turn on the console. We can see that the class name we defined has been transformed, and all the styles we defined are in styles_ Getcss()
  • Therefore, we can pass the DOM elements and CSS style content needed for class name assignment to the server, and let the server directly load the style into the HTML template
  • But how? Previously, we mentioned that static route has a context attribute, which is used to transfer data between the front and back end, so we can transfer the data through the context
  • Let’s output it directly in the home component this.props , we will find that there is a very interesting phenomenon, that is, in the browser console, output props.staticContext The value of csses is undefined, but in the server console, the output is an object, and the attribute value of csses in it is the CSS content defined previously
  • This is because although staticcontext can transfer values, it only exists between the server and the component, not between the client and the component. We can get the CSS style at the server
  • After getting the CSS style, add the CSS content as a string to the style tag of the HTML template
  • be careful:context.csses It must be an array type and push the style of each component into the array as an element, so that the CSS style of each component can take effect. However, if we directly assign the CSS style to the context.csses , then the style will be overridden. This overlay is not a style overlay, but a JS value overlay, the CSS of the first rendered component The CSS style of the component is overridden by the CSS style of the component rendered later. This is incorrect, so be sure to use arrays instead of assigning values directly
  • /src/containers/Home/index.js
componentWillMount() {
  let staticContext = this.props.staticContext;
  if (staticContext) {
    if (staticContext) {
      staticContext.csses.push(styles._getCss());
    }
  }
}
  • /src/server/render.js
export default (req, res) => {

  let context = {
    csses: []
  };

  Promise.all(promises).then(() => {
    let domContent = renderToString(
      <Provider store={store}>
        <StaticRouter context={context} location={req.path}>
          {
            renderRoutes(routes)
          }
        </StaticRouter>
      </Provider>
    );

    let cssStr = context.csses.length ? context.csses.join('\n') : '';

    let html = `
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
  <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
  <title>react-ssr</title>
  <style>${cssStr}</style>
</head>
<body>
<div id="root">${domContent}</div>
<script>
  window.context = {
    state: ${JSON.stringify(store.getState())}
  }
</script>
<script></script>
</body>
</html>
`;

    res.send(html);
  });
};
  • In this way, if you refresh the browser quickly, the page will not shake. If JS is disabled, the page style still exists
  • We can look at the page source code, and we can see that the source code of CSS is in the style tag

2. Package style components

  • If we have multiple pages, and each page has its own CSS style, then we have to write the componentwillmount hook function in each component, and pass the CSS style to staticcontext in this function. Obviously, this is not a good way, so we can encapsulate a high-level component
  • We encapsulate a high-level component price of withstyle and pass the original component and style as parameters to the high-level component
import React, { Component } from 'react';

export default (DecoratedComponent, styles) => {
  return class NewComponent extends Component {
    componentWillMount() {
      if (this.props.staticContext) {
        this.props.staticContext.csses.push(styles._getCss());
      }
    }

    render() {
      return (<DecoratedComponent {...this.props} />);
    }

  };
};
  • Use this high-level component in the home component
import WithStyle from '../../withStyle';

export default connect(mapStateToProps, mapDispatchToProps)(WithStyle(Home, styles));
  • In this way, we can encapsulate the style related functions as high-level components to improve the code reuse rate

3. Optimize components

  • We have defined a static method loaddata in the home component. This method is under the home component, but we use the withstyle high-level component to wrap the home component. Then the exported component is no longer a home component. There will be some potential problems, that is, the exported component does not have loaddata Method, then we will report an error when using it, so we can make some improvements
  • We redefine an exporthome variable, which is the return value of each high-level component after packaging. We define the loaddata method on the exporthome component to ensure that the exported component must have a loaddata method
  • The reason why we did not report an error after using the connect wrapper before is that connect automatically does the conversion for us, and the loaddata method has been mounted on the exported object, so there is no error
const ExportHome = connect(mapStateToProps, mapDispatchToProps)(WithStyle(Home, styles));

ExportHome.loadData = store => store.dispatch(UserActions.getSchoolList());

export default ExportHome;
  • Therefore, this is a point that needs attention

Related reading

  • React server rendering road 01: project infrastructure construction
  • React server rendering road 02 — the simplest server rendering
  • React server rendering road 03 — routing
  • React server rendering road 04 — redux-01
  • React server rendering road 05 — redux-02
  • 06 the way to optimize service rendering
  • React server rendering road 07 — adding CSS Style
  • React server rendering road 08-404 and redirection
  • React server rendering road 09 — SEO optimization