如何在React应用服务器上渲染CSS
介绍
Next.js (React) 和 Nuxt.js (Vue) 有助于简化将应用视图呈现到服务器的过程。
您仍然需要一个解决方案,以适应开发的方式在您的 React 组件中呈现 CSS。 您还需要一个在服务器上呈现 CSS 的解决方案,以便样式可用。
在本文中,我们将讨论渲染 CSS 的挑战,然后在 Next.js 项目的服务器上使用 styled-components 和 styled-jsx。
先决条件
要完成本教程,您需要:
- Node.js 的本地开发环境。 关注【X7X】如何安装Node.js并创建本地开发环境【X76X】。
本教程已使用 Node v16.2.0、npm v7.14.0、react v17.0.2、react-dom v17.0.2、next v10.2.3、和 styled-compnents v5.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 重置和默认值,您可以在全局范围内包含样式。
内联样式
内联样式 使用 React style 属性直接应用于 DOM 元素或 React 组件。 该实现很像 HTML 内联 style 属性,但使用 JavaScript element.style API。
这是一个例子:
const titleStyle = {
fontSize: '4rem';
lineHeight: '1.6';
color: '#222';
}
<h1 style={titleStyle} {...props}>{props.children}<h1>
这种模式鼓励样式和组件组合以及重用。
但是,内联样式并没有为您提供一种处理带有伪类和目标伪元素的焦点状态悬停的方法。 对于更大更复杂的项目,您可能需要更强大的解决方案。
组件样式
组件样式 鼓励重用并帮助您更好地组合样式和组件。 您将需要一个实用程序或库来帮助您完成此操作。 样式加载器,styled-components,Glamor等。 是支持组件样式的工具示例。
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,您将观察到:
OutputHi, new Next.js project
首先,使用 Next 中的 Head 组件使用 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;
接下来,在 Next.js 项目的根目录中创建一个 static 文件夹:
mkdir static
在此目录中,添加一个 base.css 文件,其 CSS 内容如下:
静态/base.css
body {
font-family: 'Raleway', sans-serif;
color: #222;
}
在Index页面导入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-components 导入为 styled 来设置 Button 的样式。 ButtonBase 返回按钮的骨架,而 Button 返回带有样式 ButtonBase 的组件。
在Index页面导入并使用Button组件:
index.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 开发人员工具检查页面源。
内容被呈现给服务器,但它不会在与按钮相关的页面上的任何位置显示任何样式。 另一方面,我们可以从应用的字体样式中观察到,我们知道外部文件已成功呈现到服务器。
那么组件样式出了什么问题呢? 看一下控制台:
OutputWarning: Prop `className` did not match. Server: "sc-gtsrHT kbmjhF" Client: "sc-bdnxRM kbyRfM"
您可以观察到在类名中显示不匹配的错误。 这是因为,当您重新加载时,首先从服务器获取内容。
不幸的是,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;
在我们想要设置样式的组件中根元素的结束标记之前,您可以使用 jsx 属性创建一个 style 元素。 在 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-components 和 styled-jsx。
如果您想了解有关 Next.js 的更多信息,请尝试我们的 Next.js 入门 技术讲座,或查看 我们的 Next.js 主题页面 以了解练习和编程项目.