作为 Write for DOnations 计划的一部分,作者选择了 Creative Commons 来接受捐赠。
介绍
在本教程中,您将学习为 React 组件设置样式的三种不同方法:普通 层叠样式表 (CSS)、使用 JavaScript 样式对象的内联样式、和 JSS,一个使用 JavaScript 创建 CSS 的库。 这些选项各有优缺点,有些可以为您提供更多防止样式冲突的保护,或者允许您直接引用道具或其他动态数据。 但是所有选项都有一个共同点:它们使您可以将组件特定的样式保持在组件附近,从而使组件更容易在项目中甚至在许多不相关的项目中重用。
这些选项中的每一个都依赖于 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 和创建本地开发环境中的步骤或 的 使用 PPA 部分安装如何在 Ubuntu 18.04 上安装 Node.js。
- 您将需要能够使用 Create React App 创建应用程序。 你可以在 How To Set Up a React Project with Create React App 找到使用 Create React App 安装应用程序的说明。
- 您将使用 React 组件,您可以在我们的 How To Create Custom Components in React 教程中了解这些内容。
- 您还需要 JavaScript 的基本知识,您可以在 如何在 JavaScript 系列中找到这些知识,以及 HTML 和 CSS 的基本知识。 HTML 和 CSS 的一个很好的资源是 Mozilla 开发者网络 。
第 1 步——创建一个空项目
在这一步中,您将使用 Create React App 创建一个新项目。 然后,您将删除引导项目时安装的示例项目和相关文件。 最后,您将创建一个简单的文件结构来组织您的组件。 这将为您构建本教程的示例应用程序以在下一步中进行样式设置提供坚实的基础。
首先,创建一个新项目。 在您的终端中,运行以下脚本以使用 create-react-app
安装新项目:
npx create-react-app styling-tutorial
项目完成后,进入目录:
cd styling-tutorial
在新的终端选项卡或窗口中,使用 Create React App 启动脚本 启动项目。 浏览器将自动刷新更改,因此请在您工作时保持此脚本运行:
npm start
您将获得一个正在运行的本地服务器。 如果项目没有在浏览器窗口中打开,您可以使用 http://localhost:3000/ 打开它。 如果您从远程服务器运行它,地址将是 http://your_domain:3000
。
您的浏览器将加载一个简单的 React 应用程序,该应用程序包含在 Create React App 中:
您将构建一组全新的自定义组件,因此您需要首先清除一些样板代码,以便您可以拥有一个空项目。
首先,在文本编辑器中打开 src/App.js
。 这是注入页面的根组件。 所有组件都将从这里开始。 你可以在 How To Set Up a React Project with Create React App 中找到有关 App.js
的更多信息。
使用以下命令打开 src/App.js
:
nano src/App.js
你会看到一个像这样的文件:
样式教程/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
语句中的所有内容以返回一组空标签:<></>
。 这将为您提供一个不返回任何内容的有效页面。 最终代码将如下所示:
样式教程/src/App.js
import React from 'react'; import './App.css'; function App() { return <></>; } export default App;
保存并退出文本编辑器。
最后,删除标志。 您不会在应用程序中使用它,您应该在工作时删除未使用的文件。 从长远来看,它将使您免于困惑。
在终端窗口中键入以下命令:
rm src/logo.svg
如果您查看浏览器,您将看到一个空白屏幕。
现在您已经清除了示例 Create React App 项目,创建一个简单的文件结构。 这将帮助您保持组件的独立性和独立性。
在 src
目录下创建一个名为 components
的目录。 这将包含您所有的自定义组件。
mkdir src/components
每个组件都有自己的目录来存储组件文件以及样式、图像和测试。
为 App
创建一个目录:
mkdir src/components/App
将所有 App
文件移动到该目录中。 使用通配符 *
选择以 App.
开头的任何文件,无论文件扩展名如何。 然后使用 mv
命令将它们放入新目录:
mv src/App.* src/components/App
接下来,更新index.js
中的相对导入路径,这是引导整个过程的根组件:
nano src/index.js
导入语句需要指向App
目录下的App.js
文件,所以做如下高亮修改:
样式教程/src/index.js
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
组件的组件,以查看样式如何影响子组件以及如何使用道具动态更改样式。
在这一步结束时,您将创建几个使用直接导入组件的纯 CSS 的组件。
构建警报组件
首先,创建一个新的 Alert
组件。 首先,制作目录:
mkdir src/components/Alert
接下来,打开Alert.js
:
nano src/components/Alert/Alert.js
添加一个返回字符串 Alert
的基本组件:
样式教程/src/components/Alert/Alert.js
import React from 'react'; export default function Alert() { return <div>Alert</div> }
保存并关闭文件。
接下来,打开App.js
:
nano src/components/App/App.js
导入 Alert
组件并通过添加突出显示的代码将其渲染到 <div>
中:
样式教程/src/components/App/App.js
import React from 'react'; import './App.css'; import Alert from '../Alert/Alert'; function App() { return ( <div className="wrapper"> <Alert /> </div> ) } export default App;
在此代码中,您为 <div>
提供了 wrapper
的 className
,稍后将用于样式设置。
保存并关闭文件。 当你这样做时,浏览器会刷新,你会看到你的组件:
接下来,您将设置 App
组件的样式,给它一些填充,这样 Alert
组件就不会太靠近边缘。 打开App.css
文件:
nano src/components/App/App.css
此文件使用标准 CSS。 要将填充添加到包装器,请将默认代码替换为规则,就像在纯 HTML 项目中的 CSS 一样。 在这种情况下,添加 20px
的 padding
:
样式教程/src/components/App/App.css
.wrapper { padding: 20px; }
保存并关闭文件。 当你这样做时,页面将刷新,你会看到额外的填充:
当您使用 Create React App 时,webpack 将采用导入的 CSS 并将其添加到浏览器中呈现的文件顶部的样式标记中。 如果您查看页面源代码中的 <head>
元素,您将看到以下样式:
这意味着您可以将 CSS 保留在组件旁边,并将在 构建阶段 期间将其收集在一起。 这也意味着您的样式在范围内是全局的,这可能会产生潜在的名称冲突。 使用这种方法,每个类名在所有组件中都需要是唯一的。
为了探究这个问题,您将对 Alert
组件进行一些更改。
首先,打开文件:
nano src/components/Alert/Alert.js
然后添加一些将 children
、type
和 title
作为 props 的 React 代码:
样式教程/src/components/Alert/Alert.js
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 类型系统 的成功和错误警报。
保存并关闭文件。 接下来,更新 App
中的 Alert
组件以使用新的道具。
首先,打开App.js
:
nano src/components/App/App.js
发出警报,通知用户尝试将商品添加到购物车失败:
样式教程/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
,然后添加了 error
的 type
。
保存并关闭文件。 当你这样做时,浏览器将刷新,你会看到你的新组件:
您的警报正在呈现,因此下一步是使用 CSS 设置组件样式。
将 CSS 添加到 Alert
组件
由于 Alert
组件显示错误,您将添加一个边框并将边框的颜色设置为红色阴影。 您还将为 <h2>
标签赋予相同的颜色。 但这会带来一个问题:您不能在 Alert
组件的外部 <div>
上使用名称 wrapper
,因为该名称已被 [ X160X] 组件。
类名冲突在 CSS 中并不是一个新问题,并且已经有许多尝试使用命名约定来解决它,例如 BEM。 但是命名约定可能会变得冗长,并且偶尔仍会在具有大量组件的项目中导致冲突。
在本教程中,您将使用组件名称作为 wrapper
类名称的前缀,而不是使用由命名约定分隔的特定规则集。 您的新班级名称将是 alert-wrapper
。 此外,您将添加警报的 type
作为一个类。
打开 Alert
组件:
nano src/components/Alert/Alert.js
接下来,添加以下突出显示的代码:
样式教程/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
变量组合成一个字符串。
保存并关闭文件。 现在你有了一个唯一的类名,它会根据道具动态变化。 此代码中的 JSX 将解析为具有 alert-wrapper
和 error
类名的 div
。 编译后的标记将是:<div class="alert-wrapper error">
。
现在添加样式。 首先,打开 Alert
组件的 CSS:
nano src/components/Alert/Alert.css
将以下 CSS 添加到 alert-wrapper
、success
和 error
类:
样式教程/src/components/Alert/Alert.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; }
此代码为 alert-wrapper
添加了一些边距和填充。 然后,它使用 十六进制颜色代码 #F56260
为 error
类添加带红色阴影的边框和绿色阴影 (#6DA06F
)对于 success
类。 它还会根据父级将 <h2>
颜色更新为红色或绿色。
保存并关闭文件。 当您这样做时,浏览器将刷新,您将看到新样式:
现在您已经有了一个样式化的 Alert
组件,您可以创建一个新组件来显示 Alert
组件中的项目列表。 由于孩子会更复杂,风格冲突的可能性也会更大。
创建成功消息组件
首先,为新组件 CartSuccess
创建一个目录:
mkdir src/components/CartSuccess
打开CartSuccess.js
:
nano src/components/CartSuccess/CartSuccess.js
在组件内部,导入 Alert
组件并传递一个 <div>
,其中包含用户添加到购物车的一系列商品:
样式教程/src/components/CartSuccess/CartSuccess.js
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> ) }
请注意您需要如何为外部 <div>
创建一个唯一的类名 — cart-success-wrapper
。 保存并关闭文件。
接下来,将一些 CSS 添加到自定义消息中。 打开CartSuccess.css
:
nano src/components/CartSuccess/CartSuccess.css
将 flex 的 display 添加到包装器。 您会希望大多数项目都被换行,除了 <h2>
元素,它应该占据整个宽度:
样式教程/src/components/CartSuccess/CartSuccess.css
.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
组件之后,如高亮代码所示:
样式教程/src/components/App/App.js
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;
保存并关闭文件。 当你这样做时,浏览器将刷新,你会看到你的新组件:
这按预期显示了新颜色和消息,但嵌套组件收到了一些意想不到的样式。 Alert
组件中 <h2>
的规则正在应用于 children
属性中的嵌套 <h2>
标记。
意外的样式级联到子级是 CSS 的一个常见问题。 然而,由于 React 为您提供了跨项目捆绑和共享组件的机会,您更有可能将样式无意中流向子组件。
要使用纯 CSS 解决此问题,请将 Alert
组件的 <h2>
规则更具体一些。
打开Alert.css
文件:
nano src/components/Alert/Alert.css
更改规则,使 <h2>
样式仅适用于类的直接子级,而不是使用 CSS >
child 组合器 的所有子级:
样式教程/src/components/Alert/Alert.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; }
保存并关闭文件。 当您这样做时,页面将刷新,您将看到 CartSuccess
中的 <h2>
元素保留默认颜色:
现在 Alert
组件的样式只会影响直接子节点,不会应用于其他子节点或组件。 这种方法在这种情况下效果很好,但是在组件更复杂的情况下,很难编写适用于所有情况的规则而不泄漏到组件之外。
在这一步中,您使用直接导入组件的 CSS 样式表来设置组件的样式。 使用标准 CSS 为 React 元素设置样式是一种使用标准 CSS 文件创建具有关联样式的组件的快速方法。 当您处理新项目或小型项目时,易用性使其成为良好的第一步,但随着项目的增长,它可能会导致问题。
在构建组件时,您遇到了两个常见的样式问题:类名冲突和意外的样式应用。 您可以使用标准 CSS 解决这些问题,但还有其他样式方法可以为您提供以编程方式处理这些问题的工具,而不是使用命名约定。 在下一步中,您将探索使用样式对象解决这些问题。
第 3 步 — 使用样式对象设置样式
在这一步中,您将使用 样式对象 设置组件样式,这些对象是 JavaScript 对象,使用 CSS 属性作为键。 在处理组件时,您将更新键以匹配 JavaScript 语法,并学习如何根据组件道具动态设置样式属性。
单独的 CSS 是设置 HTML 样式的最常见方式。 这种方法速度很快,并且浏览器可以高效地快速一致地应用样式。 但这并不是设置 HTML 样式的唯一选择。 在标准 HTML 中,您可以使用 style 属性 直接在元素上设置内联样式,其中包含您要应用的样式的字符串。
样式对象的最佳用途之一是动态计算样式。 如果您需要知道元素的当前位置,这将特别有用,因为在渲染元素之前无法确定,因此只能动态处理。
手动编写样式字符串很难做到,并且会引入错误。 缺少颜色或分号会破坏整个字符串。 幸运的是,在 JSX 中,您不仅限于字符串。 style 属性也可以接受包含样式的对象。 这些样式名称需要是 camelCase 而不是 kebab-case
。
像这样使用内联样式的最大优势在于,由于您使用 JavaScript 构建样式,因此您可以动态设置 CSS 属性而不是动态设置类。 这意味着您可以编写完全没有 CSS 类的代码,避免任何潜在的名称冲突,并允许您在运行时计算样式。
要使用样式对象,首先要重构 App.js
。 首先,打开文件:
nano src/components/App/App.js
在组件内部,删除导入的 App.css
文件,然后创建一个 padding
为 20
的对象,并使用style
属性:
样式教程/src/components/App/App.js
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
) 并为之前具有类的每个项目创建一个样式对象:
样式教程/src/components/CartSuccess/CartSuccess.js
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,您可以使用 变量 ,这是一项重大改进,但根据您的浏览器支持要求,可能无法完全使用。 特别是,任何版本的 Internet Explorer 都不支持它们,尽管您可以使用 polyfill 来添加支持。
由于样式对象是在运行时创建的,因此它们更具可预测性并且可以使用任何受支持的 JavaScript。
要了解样式对象如何在这种情况下提供帮助,请重构 Alert.js
以使用样式对象。 首先,打开Alert.js
:
nano src/components/Alert/Alert.js
在 Alert.js
中,删除 import './Alert.css';
并创建一个名为 colors
的对象,该对象具有错误颜色的属性和成功颜色的属性。 然后使用 type
属性将 CSS 转换为 JavaScript 对象以动态设置颜色:
样式教程/src/components/Alert/Alert.js
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 props 创建样式。
首先,安装 React 特定版本的 JSS。 本教程将使用版本 10.1.1
:
npm install react-jss
该软件包将安装几个依赖项,包括一些 JSS 插件,它们将使您能够编写简洁的样式规则。
安装完成后,您将看到一条成功消息:
Output+ react-jss@10.1.1 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 有两个步骤。 首先,您必须导入一个函数来创建一个 自定义挂钩 。 Hooks 是 React 将在每个组件渲染上运行的函数。 使用 JSS,您必须通过在组件外部传入样式定义来创建挂钩。 这将阻止代码在每次重新渲染时运行; 由于样式定义是静态的,因此没有理由多次运行代码。
通过进行突出显示的更改来创建钩子和样式对象:
样式教程/src/components/App/App.js
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
——是创建动态类名的基础。
创建钩子后,您可以通过执行组件内的函数来使用它。 这会注册钩子并动态创建样式。 进行以下突出显示的更改:
样式教程/src/components/App/App.js
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
。 这会将样式应用于作为 .wrapper
类的子级的所有 <h2>
元素。
使用 JSS,您可以通过在包含元素内创建另一个对象来创建类似的规则。 要将它们链接起来,请以 &
符号开始对象名称:
样式教程/src/components/CartSuccess/CartSuccess.js
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
属性。
保存文件。 请注意,您在此组件和 App
组件中使用了相同的名称 — wrapper
。 但是当浏览器重新加载时,不会出现命名冲突; 一切看起来都是正确的。 如果您检查浏览器中的元素,您会发现即使它们以相同的名称开头,它们都有一个唯一的类:
在这种情况下,外部组件的类是 wrapper-0-2-1
,它是在 App
组件中生成的。 CartSuccess
的类是 wrapper-0-2-3
。 您的组件名称可能略有不同,但它们将是唯一的。
在某些情况下,您可能需要制作特定的选择器来覆盖其他样式。 例如,假设您只希望在元素是 wrapper
类的子元素时应用 item
样式。 为此,首先在没有属性的对象上创建类。 然后在 wrapper
类中,使用 $
符号引用新类:
样式教程/src/components/CartSuccess/CartSuccess.js
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 将更具体地应用于 wrapper 组件下的项目:
JSS 使您能够创建具有与使用常规 CSS 创建的相同关注级别的规则,但在创建不会冲突的唯一类名称时会这样做。
JSS 的最后一个优势是您可以使用变量和其他 JavaScript 语言特性。 由于您使用的是 react-jss
,因此您可以将道具传递给样式对象以创建动态样式。 为了测试这一点,重构 Alert.js
组件以使用道具和变量来创建动态属性。
首先,打开文件:
nano src/components/Alert/Alert.js
像在上次重构代码中所做的那样创建一个样式对象。 确保将定义颜色的对象移到组件函数之外,使其与 createUseStyles
函数位于相同的 scope 中:
样式教程/src/components/Alert/Alert.js
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> ) } ...
要传递道具,您需要将样式规则设为函数。 该函数接受道具作为参数,然后返回一个规则。 要创建动态边框,请添加 border
作为属性名称和一个 箭头函数 ,它采用 type
并返回一个字符串:({ type }) => `${colors[type]} solid 1px`,
。 然后在你创建你的钩子之后,你传入你想要在创建类对象时引用的道具。 和以前一样,您按元素设置 <h2>
标记的样式,而不是创建特定的类。 您还可以为 margin
传递一个值数组,而不是像 0px 0px 10px 10px
这样的字符串。
保存文件。 请注意,您不必将所有道具传递给函数。 在这种情况下,您只想使用 type
,这就是您需要传递的全部内容。 但是,您可以传递更多甚至传递未知道具,使用 rest 运算符收集道具,然后将它们作为一组传递。 您确实需要将其作为对象传递; 但是,由于这是传递 props 的标准方式,因此将来扩展参数会更容易。
当页面重新加载时,您会看到正确的颜色,但会有一个小问题:绿色的成功颜色现在正在更新 CartSuccess
中的 <h2>
元素:
JSS 解决了很多问题,但它仍然创建了标准的 CSS。 这意味着如果您不小心,样式可以应用于子元素。 要解决此问题,请添加 >
符号以使 CSS 仅适用于直接子级:
样式教程/src/components/Alert/Alert.js
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 }) { ... } ...
保存并关闭文件。 当您这样做时,浏览器将重新加载,您将看到正确的样式:
除了本教程所涵盖的内容之外,JSS 还有更多内容。 我们没有提到的一个重要优势是 theming。 JSS 使您能够根据预定义的主题对象创建样式。 这意味着您可以将警报边框设为 alert
颜色,而不是从硬编码值创建红色,这可能是红色阴影,但可能会因主题定义而异。 这在创建白标产品或创建需要跨项目工作的可重用组件时很有用。
在这一步中,您使用名为 react-jss
的第三方库对组件进行样式设置。 您还创建了样式对象并使用 JSS 将这些对象转换为动态类以避免与其他组件发生冲突。 使用这种方法,您可以安全地重用简单的类名,而不必担心后面代码中的冲突。 最后,您学习了如何使用函数和道具创建样式以构建引用组件道具的动态样式。
结论
在本教程中,您已经开发了几个使用不同样式技术的可重用组件。 您已经了解了样式对象和 JSS 如何使用与标准 CSS 属性非常相似的名称创建对象,并创建了可以根据传入属性动态设置样式的组件。 您还了解了不同的方法如何为处理名称冲突和可重用性提供不同的选项。
与大多数 React 技术一样,没有单一的最佳解决方案。 相反,您可以选择最适合您的项目的样式选项。 有了这些选项,您可以从简单的东西开始,随着项目的增长或需求的变化进行重构,同时对您的组件将继续满足您的风格目标充满信心。
如果您想查看更多 React 教程,请查看我们的 React 主题页面,或返回 如何在 React.js 系列页面中编码 。