作者选择Creative Commons接受捐赠,作为Write for DOnations计划的一部分。
介绍
在本教程中,您将学习三种不同的方式来设置React组件的样式:普通级联样式表 (CSS)、带有JavaScript 样式对象的内联样式和JSS,一个使用JavaScript创建 CSS 的库。这些选项各有优缺点,有些可以为您提供更多防止样式冲突的保护,或者允许您直接引用 props 或其他动态数据。但是所有选项都有一个共同点:它们让您将特定于组件的样式保持在组件附近,从而使组件更容易在一个项目中甚至在许多不相关的项目中重用。
这些选项中的每一个都依赖于 CSS 属性。要使用没有任何运行时数据的纯 CSS,您可以导入样式表。如果要创建与组件集成的样式,可以使用内联样式对象,这些对象使用 CSS 属性名称作为键,样式作为值。最后,如果你想要一个组合,你可以使用第三方库,比如 JSS,用 JavaScript 语法编写你的 CSS,这个软件概念被称为CSS-in-JS。
为了说明这些方法,您将构建一个示例alert
组件,该组件将根据prop显示成功样式或错误样式。该alert
组件将采用任意数量的子项。这意味着您需要对样式冲突保持谨慎,因为您无法知道子组件将具有什么样式。创建alert
组件后,您将使用每个样式选项对其进行重构,以便您可以看到这些方法之间的异同。
先决条件
-
你需要一个运行Node.js的开发环境;本教程在 Node.js 版本 10.20.1 和 npm 版本 6.14.4 上进行了测试。要在 macOS 或 Ubuntu 18.04 上安装它,请按照如何在 macOS 上安装 Node.js 和创建本地开发环境或如何在 Ubuntu 18.04 上安装 Node.js 的使用 PPA 安装部分中的步骤进行操作。
-
您将需要能够使用Create React App创建应用程序。您可以在如何使用 Create React App设置 React 项目中找到使用 Create React App 安装应用程序的说明。
-
您还需要具备 JavaScript 的基本知识(可以在“如何在 JavaScript 中编码”系列中找到),以及 HTML 和 CSS 的基本知识。关于 HTML 和 CSS 的一个很好的资源是Mozilla Developer Network。
第 1 步 – 创建一个空项目
在这一步中,您将使用Create React App创建一个新项目。然后,您将删除引导项目时安装的示例项目和相关文件。最后,您将创建一个简单的文件结构来组织您的组件。这将为您在下一步中构建本教程的示例应用程序提供坚实的基础。
首先,创建一个新项目。在您的终端中,运行以下脚本以使用以下命令安装新项目create-react-app
:
- npx create-react-app styling-tutorial
项目完成后,切换到目录:
- cd styling-tutorial
在新的终端选项卡或窗口中,使用Create React App start script启动项目。浏览器将自动刷新更改,因此在您工作时保持此脚本运行:
- npm start
您将获得一个正在运行的本地服务器。如果项目没有在浏览器窗口中打开,您可以使用http://localhost:3000/
. 如果您从远程服务器运行它,则地址将为.http://your_domain:3000
您的浏览器将加载一个简单的 React 应用程序,该应用程序包含在 Create React App 中:
您将构建一组全新的自定义组件,因此您需要先清除一些样板代码,以便您可以拥有一个空项目。
首先,src/App.js
在文本编辑器中打开。这是注入页面的根组件。所有组件将从这里开始。您可以App.js
在如何使用 Create React App 设置 React 项目中找到更多信息。
src/App.js
使用以下命令打开:
- nano src/App.js
你会看到一个这样的文件:
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
删除该行import logo from './logo.svg';
。然后替换return
语句中的所有内容以返回一组空标签:<></>
。这将为您提供一个不返回任何内容的有效页面。最终代码将如下所示:
import React from 'react';
import './App.css';
function App() {
return <></>;
}
export default App;
保存并退出文本编辑器。
最后,删除标志。您不会在您的应用程序中使用它,您应该在工作时删除未使用的文件。从长远来看,这将使您免于困惑。
在终端窗口中输入以下命令:
- rm src/logo.svg
如果您查看浏览器,您将看到一个空白屏幕。
现在您已经清除了示例 Create React App 项目,创建一个简单的文件结构。这将帮助您保持组件隔离和独立。
在目录components
中创建一个名为的src
目录。这将保存您所有的自定义组件。
- mkdir src/components
每个组件都有自己的目录来存储组件文件以及样式、图像和测试。
创建一个目录App
:
- mkdir src/components/App
将所有App
文件移动到该目录中。使用通配符 ,*
选择以不App.
考虑文件扩展名的任何文件。然后使用mv
命令将它们放入新目录:
- mv src/App.* src/components/App
接下来,更新 中的相对导入路径index.js
,它是引导整个过程的根组件:
- nano src/index.js
import 语句需要指向目录App.js
中的App
文件,因此进行以下突出显示的更改:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App/App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
保存并退出文件。
现在项目已设置,您可以创建您的第一个组件。
第 2 步 – 使用纯 CSS 设置组件样式
在此步骤中,您将构建一个示例Alert
组件,该组件将在网页上显示警报。您将使用纯 CSS 设置样式,并将其直接导入到组件中。这将确保组件的样式与组件的 JavaScript 和JSX保持紧密耦合。您还将创建一个组件,该Alert
组件将实现该组件以查看样式如何影响子项以及如何使用 props 动态更改样式。
在这一步结束时,您将创建几个使用直接导入组件的纯 CSS 的组件。
构建警报组件
首先,创建一个新Alert
组件。首先,制作目录:
- mkdir src/components/Alert
接下来,打开Alert.js
:
- nano src/components/Alert/Alert.js
添加一个返回字符串的基本组件Alert
:
import React from 'react';
export default function Alert() {
return <div>Alert</div>
}
保存并关闭文件。
接下来,打开App.js
:
- nano src/components/App/App.js
导入Alert
组件并<div>
通过添加突出显示的代码将其渲染在 a 中:
import React from 'react';
import './App.css';
import Alert from '../Alert/Alert';
function App() {
return (
<div className="wrapper">
<Alert />
</div>
)
}
export default App;
在这段代码中,您给出了<div>
a className
of wrapper
,稍后将用于样式设置。
保存并关闭文件。当您这样做时,浏览器将刷新,您将看到您的组件:
接下来,您将设置App
组件样式以为其添加一些填充,以便Alert
组件不会太靠近边缘。打开App.css
文件:
- nano src/components/App/App.css
此文件使用标准 CSS。要将填充添加到包装器,请使用规则替换默认代码,就像在纯 HTML 项目中使用 CSS 一样。在这种情况下,加padding
的20px
:
.wrapper {
padding: 20px;
}
保存并关闭文件。当你这样做时,页面将刷新,你会看到额外的填充:
当您使用 Create React App 时,webpack将获取导入的 CSS 并将其添加到浏览器中呈现的文件顶部的样式标签中。如果您查看<head>
页面源代码中的元素,您将看到样式:
这意味着您可以将 CSS 与组件放在一起,并在构建阶段将其收集在一起。这也意味着您的样式在范围内是全局的,这可能会产生潜在的名称冲突。使用此方法,每个类名在所有组件中都需要是唯一的。
要探索这个问题,您将对Alert
组件进行一些更改。
首先,打开文件:
- nano src/components/Alert/Alert.js
然后添加一些代码做出反应,将采取children
,type
和title
道具:
import React from 'react';
import PropTypes from 'prop-types';
export default function Alert({ children, title, type }) {
return (
<div>
<h2>{title}</h2>
{children}
</div>
)
}
Alert.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.element),
PropTypes.element.isRequired
]),
title: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
}
在title
这段代码是一个<h2>
标记,children
将允许您显示子组件。您很快将使用该type
道具根据PropTypes
输入系统设置成功和错误警报。
保存并关闭文件。接下来,更新Alert
组件App
以使用新的道具。
首先,打开App.js
:
- nano src/components/App/App.js
发出警报,通知用户尝试将商品添加到购物车失败:
import React from 'react';
import './App.css';
import Alert from '../Alert/Alert';
function App() {
return (
<div className="wrapper">
<Alert title="Items Not Added" type="error">
<div>Your items are out of stock.</div>
</Alert>
</div>
)
}
export default App;
在此代码,您更新title
,并children
以失败消息,然后加一type
的error
。
保存并关闭文件。当你这样做时,浏览器将刷新,你会看到你的新组件:
您的警报正在呈现,因此下一步是使用 CSS 设置组件样式。
向Alert
组件添加 CSS
由于Alert
组件显示错误,您将添加边框并将边框颜色设置为红色阴影。您还将为<h2>
标签指定相同的颜色。但这会带来一个问题:您不能在组件wrapper
的外部使用该名称,因为该名称已被该组件采用。<div>
Alert
App
类名冲突在 CSS 中并不是一个新问题,并且已经有许多尝试使用命名约定(例如BEM )来解决它。但是命名约定可能会变得冗长,并且在具有大量组件的项目中仍然偶尔会导致冲突。
在本教程中,您将wrapper
使用组件名称作为类名的前缀,而不是使用由命名约定分隔的特定规则集。您的新班级名称将为alert-wrapper
. 此外,您将type
作为一个类添加警报。
打开Alert
组件:
- nano src/components/Alert/Alert.js
接下来,添加以下突出显示的代码:
import React from 'react';
import PropTypes from 'prop-types';
import './Alert.css';
...
export default function Alert({ children, type, title }) {
return(
<div className={`alert-wrapper ${type}`}>
<h2>{title}</h2>
{children}
</div>
)
}
...
在这种情况下,您使用模板文字将alert-wrapper
和type
变量组合成一个字符串。
保存并关闭文件。现在你有一个唯一的类名,它会根据 prop 动态变化。该JSX在这个代码将解析为div
与类名alert-wrapper
和error
。编译标记会是这样:<div class="alert-wrapper error">
。
现在添加样式。首先,打开Alert
组件的 CSS :
- nano src/components/Alert/Alert.css
下面的CSS添加到alert-wrapper
,success
和error
类:
.alert-wrapper {
padding: 15px;
margin-bottom: 15px;
}
.alert-wrapper h2 {
margin: 0 0 10px 0;
}
.alert-wrapper.success {
border: #6DA06F solid 1px;
}
.success h2 {
color: #6DA06F;
}
.alert-wrapper.error {
border: #F56260 solid 1px;
}
.error h2 {
color: #F56260;
}
此代码为alert-wrapper
. 然后,它error
使用十六进制颜色代码 为类添加带有红色阴影的边框#F56260
,并#6DA06F
为success
类添加绿色阴影 ( ) 。它还<h2>
根据父级将颜色更新为红色或绿色。
保存并关闭文件。当你这样做时,浏览器将刷新,你会看到新的样式:
现在您有了一个样式化的Alert
组件,您可以创建一个新组件来显示Alert
组件内的项目列表。由于孩子会更复杂,因此风格冲突的可能性也会更大。
创建成功消息组件
首先,为新组件创建一个目录CartSuccess
:
- mkdir src/components/CartSuccess
打开CartSuccess.js
:
- nano src/components/CartSuccess/CartSuccess.js
在组件内部,导入Alert
组件并传递一个<div>
包含一系列用户添加到购物车的项目:
import React from 'react';
import Alert from '../Alert/Alert';
import './CartSuccess.css';
export default function CartSuccess() {
return(
<Alert title="Added to Cart" type="success">
<div className="cart-success-wrapper">
<h2>
You have added 3 items:
</h2>
<div className="item">
<div>Bananas</div>
<div>Quantity: 2</div>
</div>
<div className="item">
<div>Lettuce</div>
<div>Quantity: 1</div>
</div>
</div>
</Alert>
)
}
请注意您需要如何cart-success-wrapper
为外部创建唯一的类名<div>
。保存并关闭文件。
接下来,向自定义消息添加一些 CSS。打开CartSuccess.css
:
- nano src/components/CartSuccess/CartSuccess.css
添加display
的flex
到包装。您会希望大部分项目都被包裹,除了<h2>
应该占据整个宽度的元素:
.cart-success-wrapper {
border-top: black solid 1px;
display: flex;
flex-wrap: wrap;
}
.cart-success-wrapper h2 {
width: 100%;
}
.item {
margin-right: 20px;
}
在这里,您给出<h2>
了宽度100%
。除了弯曲元素之外,您还在消息的顶部添加了一个小边框,并为item
类添加了一个边距以在项目之间提供一些空间。
保存并关闭文件。
现在您有了一个样式化的组件,将其添加到您的App
组件中。
打开App.js
:
- nano src/components/App/App.js
导入组件并添加到当前Alert
组件之后,如高亮代码所示:
import React from 'react';
import './App.css';
import Alert from '../Alert/Alert';
import CartSuccess from '../CartSuccess/CartSuccess';
function App() {
return(
<div className="wrapper">
<Alert title="Items Not Added" type="error">
<div>Your items are out of stock.</div>
</Alert>
<CartSuccess />
</div>
)
}
export default App;
保存并关闭文件。当你这样做时,浏览器将刷新,你会看到你的新组件:
这按预期显示了新颜色和消息,但嵌套组件收到了一些意外的样式。用于该规则<h2>
的Alert
组件被施加到所述嵌套<h2>
在标签children
道具。
意外的样式级联到孩子是 CSS 的一个常见问题。然而,由于 React 为您提供了跨项目捆绑和共享组件的机会,您有更大的可能性让样式无意中流向子组件。
要使用纯 CSS 解决此问题,请<h2>
为Alert
组件设置更具体的规则。
打开Alert.css
文件:
- nano src/components/Alert/Alert.css
更改规则,使<h2>
样式仅适用于类的直接子级,而不是使用 CSS>
子组合器的所有子级:
.alert-wrapper {
padding: 15px;
margin-bottom: 15px;
}
.alert-wrapper > h2 {
margin: 0 0 10px 0;
}
.alert-wrapper.success {
border: #6da06f solid 1px;
}
.success > h2 {
color: #6da06f;
}
.alert-wrapper.error {
border: #f56260 solid 1px;
}
.error > h2 {
color: #f56260;
}
保存并关闭文件。当您这样做时,页面将刷新,您将看到保留默认颜色的<h2>
元素CartSuccess
:
现在Alert
组件的样式只会影响直接子节点,不会应用于其他子节点或组件。这种方法在这种情况下效果很好,但在组件更复杂的情况下,编写适用于所有情况的规则而不会泄漏到组件外可能很困难。
在此步骤中,您使用直接导入组件的 CSS 样式表为组件设置了样式。使用标准 CSS 样式化 React 元素是一种使用标准 CSS 文件创建具有关联样式的组件的快速方法。当您处理新项目或小型项目时,它的易用性使其成为良好的第一步,但随着项目的发展,它可能会导致问题。
在构建组件时,您遇到了两个常见的样式问题:类名冲突和意外的样式应用程序。您可以使用标准 CSS 来解决这些问题,但还有其他样式方法可以为您提供以编程方式而不是使用命名约定来处理这些问题的工具。在下一步中,您将探索使用样式对象解决这些问题。
第 3 步 – 使用样式对象进行样式设置
在这一步中,您将使用style objects 设置组件的样式,这些对象是使用 CSS 属性作为键的JavaScript 对象。在处理组件时,您将更新键以匹配 JavaScript 语法,并学习如何根据组件 props 动态设置样式属性。
单独的 CSS 是设置 HTML 样式的最常见方式。这种方法很快,浏览器在快速且一致地应用样式方面非常高效。但这并不是 HTML 样式的唯一选择。在标准 HTML 中,您可以使用style 属性和包含要应用的样式的字符串直接在元素上设置内联样式。
样式对象的最佳用途之一是动态计算样式。如果您需要知道元素的当前位置,这将特别有用,因为这在元素渲染之前无法确定,因此只能动态处理。
手动编写样式字符串很困难,并且可能会引入错误。缺少颜色或分号会破坏整个字符串。幸运的是,在 JSX 中,您不仅限于字符串。style 属性也可以接受包含样式的对象。这些样式名称需要是camelCase
而不是kebab-case
.
像这样使用内联样式的最大优势在于,由于您使用 JavaScript 构建样式,因此您可以动态设置 CSS 属性而不是动态设置类。这意味着您可以编写完全没有 CSS 类的代码,避免任何潜在的名称冲突并允许您在运行时计算样式。
要使用样式对象,请从重构开始App.js
。首先,打开文件:
- nano src/components/App/App.js
在组件内部,删除导入的App.css
文件,然后创建一个具有padding
of20
的对象并将该对象传递给<div>
usingstyle
属性:
import React from 'react';
import Alert from '../Alert/Alert';
import CartSuccess from '../CartSuccess/CartSuccess';
function App() {
const wrapper = {
padding: 20
};
return(
<div style={wrapper}>
<Alert title="Items Not Added" type="error">
<div>Your items are out of stock.</div>
</Alert>
<CartSuccess />
</div>
)
}
export default App;
请注意,您不必指定像素作为 的单位padding
。默认情况下,React 会将其转换为一串像素。如果您想要一个特定的单位,请将其作为字符串传递。因此,如果您希望填充为百分比,则为padding: '20%'
.
大多数数字将自动转换为像素。但是,也有例外。该属性line-height
可以采用没有单位的普通数字。如果您想在这种情况下使用像素单位,则需要将像素指定为字符串。
保存并关闭文件。当您这样做时,浏览器将刷新,您将看到与以前一样的页面:
接下来,重构CartSuccess
。首先,打开文件:
- nano src/components/CartSuccess/CartSuccess.js
与 一样App.js
,删除导入的 CSS ( CartSuccess.css
) 并为每个以前有一个类的项目创建一个样式对象:
import React from 'react';
import Alert from '../Alert/Alert';
export default function CartSuccess() {
const styles = {
header: {
width: '100%'
},
item: {
marginRight: 20
},
wrapper: {
borderTop: 'black solid 1px',
display: 'flex',
flexWrap: 'wrap'
}
}
return(
<Alert title="Added to Cart" type="success">
<div style={styles.wrapper}>
<h2 style={styles.header}>
You have added 3 items:
</h2>
<div style={styles.item}>
<div>Bananas</div>
<div>Quantity: 2</div>
</div>
<div style={styles.item}>
<div>Lettuce</div>
<div>Quantity: 1</div>
</div>
</div>
</Alert>
)
}
在这种情况下,您没有创建多个单独的对象;相反,您创建了一个包含其他对象的单个对象。还要注意的是,你需要使用骆驼情况下的性能margin-right
,border-top
和flex-wrap
。
保存并关闭文件。当您这样做时,页面将刷新,您将看到具有相同样式的页面:
由于您没有使用类,因此您不必担心任何名称冲突。使用 JavaScript 创建样式的另一个优点是您可以利用任何 JavaScript 语法,例如变量和模板文字。使用现代 CSS,您可以使用variables,这是一项重大改进,但可能无法完全可用,具体取决于您的浏览器支持要求。特别是,它们不受任何版本的 Internet Explorer 支持,尽管您可以使用polyfill来添加支持。
由于样式对象是在运行时创建的,因此它们更具可预测性并且可以使用任何受支持的 JavaScript。
要查看样式对象如何在这种情况下提供帮助,请重构Alert.js
以使用样式对象。首先,打开Alert.js
:
- nano src/components/Alert/Alert.js
在里面Alert.js
,删除import './Alert.css';
并创建一个名为的对象colors
,该对象具有错误颜色的属性和成功颜色的属性。然后使用type
prop将 CSS 转换为 JavaScript 对象以动态设置颜色:
import React from 'react';
import PropTypes from 'prop-types';
export default function Alert({ children, type, title }) {
const colors = {
success: '#6da06f',
error: '#f56260',
}
const style = {
heading: {
color: colors[type],
margin: '0 0 10px 0',
},
wrapper: {
border: `${colors[type]} solid 1px`,
marginBottom: 15,
padding: 15,
position: 'relative',
}
}
return(
<div style={style.wrapper}>
<h2 style={style.heading}>{title}</h2>
{children}
</div>
)
}
...
这里有一些变化。首先,您使用单个样式声明wrapper
,然后根据类型动态设置颜色。您不是<h2>
一般的样式元素,而是样式这些恰好是<h2>
元素的特定元素。由于您没有将样式应用于元素类型,因此样式不会流向子元素。
保存并关闭文件。当您这样做时,浏览器将刷新,您将看到应用的样式。
样式对象解决了很多问题,但它们也有缺点。首先,内联样式存在性能成本。浏览器旨在高效处理 CSS,应用内联样式的样式对象无法利用这些优化。另一个问题是将样式应用到具有样式对象的子元素更加困难。在这种情况下,您不希望将样式应用于子项,但通常情况下您确实希望样式级联。例如,<h2>
如果您使用不太具体的样式策略,在每个元素上设置自定义字体系列或对每个元素应用自定义大小会更容易。
然而,在这些方法之间有一个中间立场。一些第三方库旨在找到这个中间立场。在下一步中,您将使用称为 CSS-in-JS 的混合方法使用称为 JSS 的库创建样式。
第 4 步 — 使用 JSS 进行样式设置
在这一步中,您将使用流行的库JSS来设置对象的样式。您将安装新库并将样式对象转换为 JSS 对象。然后,您将重构代码以使用动态生成的类名,以防止跨模块的类名之间发生冲突。您还将构建动态设置样式并使用嵌套属性创建特定样式规则的 JavaScript 样式对象。
JSS 是一个用于创建 CSS-in-JS 的库。这种方法有许多不同的用例和选项,但本教程的主要优点是它会创建动态类名,避免组件之间的冲突。您还将能够利用 JavaScript 语法,这意味着您将能够使用变量并基于 React 道具创建样式。
首先,安装React 特定版本的 JSS。本教程将使用版本10.1.1
:
- npm install react-jss
该软件包将安装几个依赖项,包括许多JSS 插件,它们将使您能够编写简洁的样式规则。
安装完成后,您将看到一条成功消息:
Output+ [email protected]
added 281 packages from 178 contributors, removed 142 packages, updated 1392 packages and audited 1025251 packages in 144.872s
根据您的 Node 版本和其他依赖项,您的输出会略有不同。
现在库已安装,转换App.js
为使用 JSS。首先,打开App.js
:
- nano src/components/App/App.js
使用 JSS 有两个步骤。首先,您必须导入一个函数来创建自定义 hook。钩子是 React 将在每个组件渲染上运行的函数。使用 JSS,您必须通过在组件外部传入样式定义来创建一个钩子。这将阻止代码在每次重新渲染时运行;由于样式定义是静态的,因此没有理由多次运行代码。
通过进行突出显示的更改来创建钩子和样式对象:
import React from 'react';
import { createUseStyles } from 'react-jss';
import Alert from '../Alert/Alert';
import CartSuccess from '../CartSuccess/CartSuccess';
const useStyles = createUseStyles({
wrapper: {
padding: 20,
}
});
function App() {
return(
<div>
<Alert title="Items Not Added" type="error">
<div>Your items are out of stock.</div>
</Alert>
<CartSuccess />
</div>
)
}
export default App;
请注意,在这种情况下,您的样式对象包含另一个名为 的对象wrapper
,该对象包含使用相同驼峰格式的样式。对象名称wrapper
——是创建动态类名的基础。
创建钩子后,您可以通过执行组件内的函数来使用它。这会注册钩子并动态创建样式。进行以下突出显示的更改:
import React from 'react';
import { createUseStyles } from 'react-jss'
import Alert from '../Alert/Alert';
import CartSuccess from '../CartSuccess/CartSuccess';
const useStyles = createUseStyles({
wrapper: {
padding: 20,
}
});
function App() {
const classes = useStyles()
return(
<div className={classes.wrapper}>
<Alert title="Items Not Added" type="error">
<div>Your items are out of stock.</div>
</Alert>
<CartSuccess />
</div>
)
}
export default App;
在此代码中,您调用该函数并将结果分配给名为 的变量classes
。新变量classes
将是一个包含动态类名称的对象。然后,通过使用在对象上定义的相同名称,将适当的类名应用于元素。你在这里使用了classes.wrapper
.
保存并关闭文件。当您这样做时,浏览器将刷新,您将看到与以前相同的样式。但是,如果您查看控制台,您会发现类名与您在对象中定义的名称并不完全匹配:
在这种情况下,类名是wrapper-0-2-1
,但您的类名可能不同。您还将看到样式从对象转换为 CSS 并放置在<style>
标记中。将此与内联样式形成对比,内联样式不会转换为 CSS 并且没有任何类名。
JSS 动态创建类名,因此它们不会与其他文件中的相似名称冲突。要在工作中看到这一点,请重构CartSuccess.js
以使用 JSS 样式。
打开文件:
- nano src/components/CartSuccess/CartSuccess.js
在文件中,使用createUseStyles
. <h2>
您将为<h2>
包装器内部的元素创建规则,而不是将类应用于元素。要使用普通 CSS 做到这一点,您需要在类和元素之间添加一个空格 — .wrapper h2
。这将样式应用于<h2>
作为.wrapper
该类的所有子元素的所有元素。
使用 JSS,您可以通过在包含元素内创建另一个对象来创建类似的规则。要将它们链接起来,请使用以下&
符号开始对象名称:
import React from 'react';
import { createUseStyles } from 'react-jss';
import Alert from '../Alert/Alert';
const useStyles = createUseStyles({
item: {
marginRight: 20
},
wrapper: {
borderTop: 'black solid 1px',
display: 'flex',
flexWrap: 'wrap',
'& h2': {
width: '100%'
}
}
})
export default function CartSuccess() {
const classes = useStyles();
return(
<Alert title="Added to Cart" type="success">
<div className={classes.wrapper}>
<h2>
You have added 3 items:
</h2>
<div className={classes.item}>
<div>Bananas</div>
<div>Quantity: 2</div>
</div>
<div className={classes.item}>
<div>Lettuce</div>
<div>Quantity: 1</div>
</div>
</div>
</Alert>
)
}
除了为包装器创建规则之外,您还为item
. 创建自定义挂钩后,您将自定义类名传递给className
属性。
保存文件。请注意,您wrapper
在此组件和App
组件中都使用了相同的名称。但是当浏览器重新加载时,不会出现命名冲突;一切都会看起来正确。如果您检查浏览器中的元素,您会发现尽管它们以相同的名称开头,但它们每个都有一个唯一的类:
在这种情况下,外部组件的类是wrapper-0-2-1
,它是在App
组件中生成的。的类CartSuccess
是wrapper-0-2-3
。您的组件名称可能略有不同,但它们将是唯一的。
在某些情况下,您可能需要制作一个特定的选择器来覆盖其他样式。例如,假设您只希望item
在元素是wrapper
类的子元素时应用样式。为此,首先在没有属性的对象上创建类。然后在wrapper
类内部,使用$
符号引用新类:
import React from 'react';
import { createUseStyles } from 'react-jss'
import Alert from '../Alert/Alert';
const useStyles = createUseStyles({
item: {},
wrapper: {
borderTop: 'black solid 1px',
display: 'flex',
flexWrap: 'wrap',
'& h2': {
width: '100%'
},
'& $item': {
marginRight: 20
}
}
})
export default function CartSuccess() {
const classes = useStyles()
return(
<Alert title="Added to Cart" type="success">
<div className={classes.wrapper}>
<h2>
You have added 3 items:
</h2>
<div className={classes.item}>
<div>Bananas</div>
<div>Quantity: 2</div>
</div>
<div className={classes.item}>
<div>Lettuce</div>
<div>Quantity: 1</div>
</div>
</div>
</Alert>
)
}
保存并关闭文件。当浏览器重新加载时,页面看起来是一样的,但item
CSS 将更具体地应用于包装器组件下的项目:
JSS 使您能够创建具有与使用常规 CSS 创建相同级别的焦点的规则,但会在创建不会冲突的唯一类名称时这样做。
JSS 的最后一个优势是您可以使用变量和其他 JavaScript 语言功能。由于您正在使用react-jss
,您可以将道具传递给样式对象以创建动态样式。为了测试这一点,重构Alert.js
组件以使用道具和变量来创建动态属性。
首先,打开文件:
- nano src/components/Alert/Alert.js
创建一个样式对象,就像您在上次重构代码中所做的那样。一定要移动定义的组件功能,所以它是在同一以外的颜色对象范围的createUseStyles
功能:
import React from 'react';
import PropTypes from 'prop-types';
import { createUseStyles } from 'react-jss';
const colors = {
success: '#6da06f',
error: '#f56260',
};
const useStyles = createUseStyles({
wrapper: {
border: ({ type }) => `${colors[type]} solid 1px`,
marginBottom: 15,
padding: 15,
position: 'relative',
'& h2': {
color: ({ type }) => colors[type],
margin: [0, 0, 10, 0],
}
}
});
export default function Alert({ children, type, title }) {
const classes = useStyles({ type })
return(
<div className={classes.wrapper}>
<h2>{title}</h2>
{children}
</div>
)
}
...
To pass props, you make the style rule a function. The function accepts the props as an argument then returns a rule. To create a dynamic border, you add border
as the property name and an arrow function that takes type
and returns a string: ({ type }) => `${colors[type]} solid 1px`,
. Then after you create your hook, you pass in the props you want to reference when creating the classes object. As before, you style the <h2>
tag by element instead of creating a specific class. You also pass an array of values for margin
rather than a string such as 0px 0px 10px 10px
.
Save the file. Notice that you don’t have to pass all the props into the function. In this case, you only want to use type
, so that’s all you need to pass. However, you can pass more or even pass unknown props using the rest operator to collect props and then pass them as a group. You do need to pass it as an object; however, since that’s the standard way to pass props, it will make extending the arguments easier in the future.
When the page reloads, you’ll see the correct colors, but there will be a slight problem: the green success color is now updating the <h2>
element in CartSuccess
:
JSS solves many problems, but it still creates standard CSS. That means that styles can apply to child elements if you are not careful. To fix this, add the >
symbol to make the CSS only apply to immediate children:
import React from 'react';
...
const useStyles = createUseStyles({
wrapper: {
border: ({ type }) => `${colors[type]} solid 1px`,
marginBottom: 15,
padding: 15,
position: 'relative',
'& > h2': {
color: ({ type }) => colors[type],
margin: [0, 0, 10, 0],
}
}
});
export default function Alert({ children, type, title }) {
...
}
...
Save and close the file. When you do the browser will reload and you’ll see the correct styles:
There is much more to JSS beyond what is covered in this tutorial. One important advantage that we haven’t touched on is theming. JSS gives you the ability to create styles based off of pre-defined theme objects. That means that instead of creating a color red from a hard coded value, you can make the alert border the alert
color, which will likely be a shade of red, but could be different depending on the theme definition. This is useful when creating white label products or creating reusable components that need to work across projects.
In this step, you styled components using a third-party library called react-jss
. You also created style object and used JSS to convert those objects into dynamic classes to avoid conflicting with other components. Using this method, you can safely reuse simple class names without worrying about conflicts later in the code. Finally, you learned how to create styles using functions and props to build dynamic styles that reference component props.
Conclusion
Throughout this tutorial, you have developed several reusable components that use different style techniques. You’ve learned how style objects and JSS create objects using names that closely mirror standard CSS properties, and have created components that can dynamically set styles based on incoming properties. You also learned how different approaches provide different options for handling name conflicts and reusability.
As with most React techniques, there is no single best solution. Instead, you can choose the styling option that is the best fit for your project. With these options in hand, you can start with something simple and refactor as the project grows or the requirements change, while remaining confident that your components will continue to meet your style goals.
如果您想查看更多 React 教程,请查看我们的React 主题页面,或返回如何在 React.js 中编写代码系列页面。