如何在 React 应用服务器上渲染 CSS

介绍

Next.js (React) 和Nuxt.js (Vue) 有助于简化将应用视图渲染到服务器的过程。

您仍然需要一个解决方案,以适应开发的方式在 React 组件中渲染 CSS。您还需要一个在服务器上呈现 CSS 的解决方案,以便样式可用。

在本文中,我们将讨论渲染 CSS 的挑战,然后在 Next.js 项目中在服务器上使用styled-componentsstyled-jsx

先决条件

要完成本教程,您需要:

本教程已通过 Node v16.2.0、npmv7.14.0、reactv17.0.2、react-domv17.0.2、nextv10.2.3 和styled-compnentsv5.3.0 验证。

理解 React 中的样式模式

在 React 中编写 CSS 的常用方法很少,它们都有效。根据您的情况,您可以通过以下方式之一将样式应用到 React 应用程序:

全局样式

全局样式是包含本地或内容交付网络 (CDN) 托管样式表的模式。

下面是一个例子:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.css" />

这是不太受欢迎的样式模式,因为它不鼓励组件重用。它也不鼓励样式组合。

这些 CSS 规则不直接作用于特定元素或组件。当组件以通过导入、嵌套或扩展未预料到的方式进行交互时,很可能会发生冲突。

但是,在某些情况下,您可以全局包含样式,例如字体包含和 CSS 重置和默认值。

内联样式

内联样式使用 Reactstyle属性直接应用于 DOM 元素或 React 组件实现很像 HTML 内联style属性,但使用 JavaScript element.styleAPI。

下面是一个例子:

const titleStyle = {
  fontSize: '4rem';
  lineHeight: '1.6';
  color: '#222';
}

<h1 style={titleStyle} {...props}>{props.children}<h1>

这种模式鼓励样式和组件组合以及重用。

但是,内联样式并没有为您提供一种方法来处理具有伪类和目标伪元素的焦点状态悬停。对于更大和复杂的项目,您可能需要更强大的解决方案。

组件样式

组件样式鼓励重用并帮助您更好地组合样式和组件。您将需要一个实用程序或库来帮助您完成此操作。样式加载器、styled-componentsGlamour等是支持组件样式的工具示例。

styled-components是实现 CSS-in-JS 的流行解决方案。它们是内联组件样式,但更有能力执行复杂(伪)选择、嵌套等操作。

步骤 1 – 设置项目

首先,打开您的终端并为您的项目创建一个新文件夹:

  • mkdir css-ssr-next-example

接下来,切换到新的项目目录:

  • cd css-ssr-next-example

然后运行以下命令对其进行初始化:

  • npm init -y

这将创建一个package.json文件,您将使用文件来跟踪依赖项。

然后,安装 Next.js、React 和 React DOM:

  • npm install next@10.2.3 react@17.0.2 react-dom17.0.2

此时,您将拥有一个包含 Next.js、React 和 React DOM 的新项目。

注意:自发布以来,创建 Next.js 项目的更现代方法可以使用create-next-app.

现在,更新package.json以使用dev脚本启动 Next.js 应用程序

包.json
{
  "name": "css-ssr-next-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "next"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "next": "^10.2.3",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  }
}

然后,创建一个pages目录:

  • mkdir pages

在这个目录下,添加一个index.js文件,内容如下:

页面/index.js
import React from 'react';

const Index = () => <h1>Hi, new Next.js project</h1>;

export default Index;

现在运行dev脚本来启动服务器:

  • npm run dev

如果您localhost:3000在网络浏览器中打开,您将看到:

Output
Hi, new Next.js project

首先,使用HeadNext 中组件来规范化样式normalize.css

页面/index.js
import React from 'react';
import Head from 'next/head';

const Index = () =>
  <div>
    <Head>
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.css" />
      <link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet" />
    </Head>
    <h1>Hi, new Next.js project</h1>
  </div>;

export default Index;

接下来,static在 Next.js 项目的根目录上创建一个文件夹:

  • mkdir static

在此目录中,添加base.css具有以下 CSS 内容文件:

静态/ base.css
body {
  font-family: 'Raleway', sans-serif;
  color: #222;
}

base.css在索引页面导入文件:

页面/index.js
// ...

    <Head>
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.css" />
      <link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet" />
      <link rel="stylesheet" href="/static/base.css" />
    </Head>

// ...

在浏览器中,字体将从默认字体明显更改为 Raleway。

此时,您有一个具有全局样式的 Next.js 项目。

第 2 步 – 使用样式化组件

让我们使用styled-components库来设置组件的样式。

首先,安装styled-components

  • npm install styled-components@5.3.0

接下来,创建一个components目录:

mkdir components

在此目录中,添加一个Button.js文件并使用以下内容更新该文件:

组件/Button.js
import React from 'react';
import styled from 'styled-components';

const ButtonBase = (props) => <button {...props}>{props.children}</button>
const Button = styled(ButtonBase)`
  /* Rectangle 2: */
  background: #0077E2;
  box-shadow: 0 2px 7px 0 rgba(120,137,149,0.25);
  border-radius: 3px;
  text-transform: uppercase;
  padding: 10px;
  color: #fff;
  border: #0077E2;
`

export default Button;

我们正在使用styled-componentsimport asstyled来样式 a ButtonButtonBase返回按钮的骨架,同时Button返回带有样式的组件ButtonBase

Button在索引页面导入并使用组件:

索引.js
import React from 'react';
import Head from 'next/head';
import Button from '../components/Button'

const Index = () => <div>
  <Head>
    ....
  </Head>
  <h1>Hi, new Next.js project</h1>
  <Button>Clicker</Button>
</div>;

export default Index;

保存更改并在浏览器中观察应用程序。文本下方将是一个新样式的按钮。

但是,在硬刷新后,按钮的样式不再符合预期。

按钮样式似乎丢失了,但不知何故,基本样式完好无损。使用 Web 开发人员工具检查页面源。

内容呈现给服务器,但它不会在页面上与按钮相关的任何位置显示任何样式。另一方面,从应用的字体样式中可以看出,我们知道外部文件已成功渲染到服务器。

那么组件样式出了什么问题呢?看一下控制台:

Output
Warning: Prop `className` did not match. Server: "sc-gtsrHT kbmjhF" Client: "sc-bdnxRM kbyRfM"

您可以观察到一个错误,显示类名中的不匹配。这是因为,当您重新加载时,首先从服务器获取内容。

不幸的是,styled-components不会呈现给服务器,这使它们当时不可用。

注:自发布以来,styled-components支持服务端渲染

您也可以参考Next.js 的with-styled-components示例

让我们来看看一个解决方案。

第 3 步 – 使用样式化 JSX

致力于 Next.js 的团队引入了一个名为styled-jsx帮助控制缓解此问题的库该库确保您编写的样式在服务器和浏览器上呈现,而不仅仅是在浏览器上呈现。

它已经与 Next.js 捆绑在一起,所以你不需要安装任何东西。

重新访问Button组件并修改它以使用styled-jsx

组件/Button.js
import React from 'react';

const Button = props => (
  <button {...props}>
    {props.children}
    <style jsx>{`
      background: #0077e2;
      box-shadow: 0 2px 7px 0 rgba(120, 137, 149, 0.25);
      border-radius: 3px;
      text-transform: uppercase;
      padding: 10px;
      color: #fff;
      border: #0077e2;
    `}</style>
  </button>
);

export default Button;

在我们想要设置样式的组件中根元素的结束标记之前,您可以创建一个style具有该jsx属性元素style元素中,打开一个包含模板字符串的花括号。字符串应该是有效的 CSS 样式和服务器作为您的组件样式。

styled-jsx如果您的组件不是仅由一个元素组成,您还可以使用选择器

组件/TitleAndButton.js
import React from 'react';

const TitleAndButton = props => (<div {...props}>
  <h1 className="title">Hi Title</h1>
  <button>Clicker</button>
  <style jsx>{`
    h1.title {
      color: #222
    }
    button {
      background: #0077e2;
      box-shadow: 0 2px 7px 0 rgba(120, 137, 149, 0.25);
      border-radius: 3px;
      text-transform: uppercase;
      padding: 10px;
      color: #fff;
      border: #0077e2;
    }
  `}</style>
</div>);

export default TitleAndButton;

现在,您可以TitleAndButton在“索引”页面中导入和使用该组件。这两个元素都将按预期设置样式。

注意:您也可以参考Next.js 的with-styled-jsx示例

styled-jsx 是在 Next.js 项目中将样式渲染到服务器的一种方法。

结论

在本文中,您了解了渲染 CSS 的挑战,然后在 Next.js 项目中使用styled-componentsstyled-jsx在服务器上。

如果您想了解有关 Next.js 的更多信息,请尝试我们的Next.js技术入门讲座,或查看我们的 Next.js 主题页面以获取练习和编程项目。

觉得文章有用?

点个广告表达一下你的爱意吧 !😁