专栏名称: 前端从进阶到入院
我是 ssh,只想用最简单的方式把原理讲明白。wx:sshsunlight,分享前端的前沿趋势和一些有趣的事情。
目录
相关文章推荐
中国能源报  ·  关于举办绿电、绿证、CCER交易培训的通知 ·  2 天前  
中国能源报  ·  关于举办绿电、绿证、CCER交易培训的通知 ·  2 天前  
龙船风电网  ·  建设进度过半!这座海上风电场成本上涨 ·  2 天前  
南方能源观察  ·  深化新能源上网电价市场化改革正当其时 ·  2 天前  
中国舞台美术学会  ·  通知丨文化和旅游部艺术司关于征集戏曲创作优秀 ... ·  4 天前  
51好读  ›  专栏  ›  前端从进阶到入院

styled-components,看完这篇彻底懂了!

前端从进阶到入院  · 公众号  ·  · 2024-08-23 10:50

正文

一周是一年的2%

前言

最近在做项目梳理,然后无意中在一些国外的UI库中发现如下的代码示例。

大家仔细观察上面的代码,其实就是对常规的布局做了封装,我们可以通过通过 {chilren} 将对应的组件进行包裹。

这样做的好处就是

  1. 见名知意,通过组件的名称我们就可以知晓该页面使用了何种布局
  2. 布局样式和组件内部样式进行分割
  3. 统一管理

然后,它背后用的技术就是我们在 CSS-in-JS 。针对 CSS-in-JS 业界是褒贬不一。

上面列举了 CSS-in-JS 的各种利弊。这其实就是仁者见仁,智者见智。但是,我更看中它在抽离公共布局方面的应用。就像最开头的截图所示,我们可以不把现有项目中所有组件都 css-in-js 处理,但是我们可以对系统种常规布局进行抽离,这样我们项目就层级就更加清晰明了。

既然,它是有用的,那么我们今天就来聊聊 CSS-in-JS 。因为, CSS-in-JS 有很多解决方案。(emotion [1] /styled-components [2] )。

下面,我们就挑业界比较受欢迎的 styled-components 来进行讲解。

好了,天不早了,干点正事哇。

我们能所学到的知识点

  1. 初始化项目
  2. 基本用法
  3. 使用 Props
  4. 扩展样式
  5. 嵌套样式
  6. 扩展 React 组件
  7. CSS变量
  8. 添加主题
  9. 处理动画
  10. 使用 as 属性
  11. 默认属性

Styled-components [3] 是一个库,它允许你在构建 Reactjs 自定义组件时,使用 JavaScript CSS

1. 初始化项目

由于我们这里是一个技术讲解的文章,不需要额外的配置,所以我们就不用我们的f_cli [4] 来构建项目了,我们就用最简单的方式( cra )来构建项目(当然也可以使用 vite )

npx create-react-app styled_demo

由于每个脚手架都有自己内置的逻辑,我们需要删除一些默认的逻辑。在初始化后,我们只保留 index.js app.js 。并且对其做一些简单的修改,使其更适合我们的需求。

App.js

function App({
  return <h1>Hello, Front789!h1>;
}

export default App;

index.js

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <>
    <App />
  >

);

安装 styled-components

安装 styled-components 的命令如下:

npm install styled-components

虽然我们这个项目就寥寥几个文件,但是它已经支持了 styled-components 的功能了。下面,我们就来学习一下它是如何工作的。


2. 基本用法

app.js 中,

  • 使用

    标签创建一个标题
  • 使用

    标签创建一个段落
  • 使用 标签创建一个按钮
function App({
  return (
    <div>
      <h1>Front789h1>
      <p>专注于前端开发技术/Rust及AI应用知识分享的Coderp>
      <button>我是按钮!button>
    div>

  );
}

export default App;

我们需要在 app.js 文件中导入 styled-components

import styled from "styled-components";

然后我们将创建我们的 自定义组件 H1 ,并使用它代替

标签,并添加自定义样式。

const H1 = styled.h1`
  color: red;
  font-size: 4rem;
`
;
  • 首先,我们需要给它一个自定义的名称( H1 )。
  • 然后,我们将从 styled. 开始,并用 「反引号」 括起样式。

现在,当我们使用这个 自定义组件 时,它将具有带有样式的 属性。

import styled from "styled-components";

const H1 = styled.h1`
  color: red;
  font-size: 4rem;
`
;

function App({
  return (
    <div>
      <H1>Front789H1>
      <p>专注于前端开发技术/Rust及AI应用知识分享的Coderp>
      <button>我是按钮!button>
    div>

  );
}

export default App;

我们在页面中就会看多对应的效果图。

上面有几个点需要注意

  1. 我们使用了 styled.h1 来创建 H1 ,此时 H1 就是一个自定义组件,在 React 中, 始终使用 「大写字母」 来自定义组件名称
  2. 我们在浏览器 DevTool->Elements 种看到,与 H1 对应的 h1 元素自动添加了一个 class ,并且其值是一组 hash 值,这样做是为了避免 「命名冲突」

现在让我们为我们的按钮组件添加样式:

const DefaultButton = styled.button`
  background-color: #645cfc;
  border: none;
  padding: 10px;
  color: white;
`
;

DefaultButton 是我们的自定义组件名称。在我们给它样式之后,我们可以给它任何我们想要的 HTML 标签,以便这个自定义组件将拥有该标签。

现在我们将使用上面创建的 DefaultButton 作为我们的自定义组件在 React.js 中使用。

import styled from "styled-components";

const H1 = styled.h1`
  color: red;
`
;

const DefaultButton = styled.button`
  background-color: #645cfc;
  border: none;
  padding: 10px;
  color: white;
`
;

function App({
  return (
    <div>
      <H1>Front789H1>
      <p>专注于前端开发技术/Rust及AI应用知识分享的Coderp>
      <DefaultButton>我是按钮!DefaultButton>
    div>

  );
}

export default App;

我们也可以通过为每个不同的组件在 styled-components 中创建一个不同的文件来保持我们的文件清晰。

我们将在 src 中创建一个名为 components 的新文件夹,并创建文件 Title.js Buttons.js 来分离标题和按钮的样式。

我们将 H1 样式复制并粘贴到 Title.js 中,并将 DefaultButton 样式复制并粘贴到 Buttons.js 中。

现在我们的 App.js 看起来是这样的:

import H1 from "./components/Title";
import { DefaultButton } from "./components/Buttons";

function App({
  return (
    <div>
      <H1>Front789H1>
      <p>专注于前端开发技术/Rust及AI应用知识分享的Coderp>
      <DefaultButton>我是按钮!DefaultButton>
    div>

  );
}

export default App;

3. 使用 Props

对于 React 组件来讲, Props 是一个至关重要的特性,通过 Props 我们可以从组件调用处向组件内部传入对应的运行时参数,然后基于运行时的逻辑进行展示操作。

使用 styled components 定义的组件也可以接受 props

import H1 from './components/Title'
import {DefaultButton} from './components/Buttons'


function App({
  return (
    <div>
      <H1>Front789H1>
      <p>专注于前端开发技术/Rust及AI应用知识分享的Coderp>
      <DefaultButton>我是按钮!DefaultButton>
      <DefaultButton red>带属性的按钮!DefaultButton>
    div>

  );
}

export default App;

在上面的代码中,我创建了另一个 DefaultButton 。但是相较于之前的 DefaultButton ,第二个 DefaultButton 拥有了额外的属性 red

也就是说,我们希望第二个 DefaultButton 在运行时执行额外的展示逻辑。

import styled from 'styled-components'

export const DefaultButton = styled.button`
background-color:  ${(props) => (props.red && 'red') || '#645cfc'};
border: none;
padding: 10px;
color: white;
`

我们在使用 styled-components 定义组件时,使用了 模板字面量 也就意味着可以在其中写 JavaScript 。在这些大括号中,我们声明了一个箭头函数,它有一个 props 参数,可以访问自定义组件的属性。箭头函数表示如果给定了 red 属性,则背景颜色应为红色,否则应为蓝莓色。

当然,我们还可以通过对 props 进行解构处理,通过 {} 和属性名称来解构 props

与其使用 props.red 进行访问,我们可以。

import styled from 'styled-components'

export const DefaultButton = styled.button`
background-color:  ${({red}) => (red && 'red') || '#645cfc'};
border: none;
padding: 10px;
color: white;
`

现在我们可以直接使用 red 而不是 props.red


4. 扩展样式

通过上述的操作,我们已经拥有了一定样式封装能力的自定义组件了。此时,我们想在之前组件的基础上进行二次封装。从语言开发的角度来讲,就是我们想继承之前的样式,并且做额外的操作。此时我们可以使用在 styled components 中扩展样式来实现。

我们只需要简单一步操作即可完成。之前我们是用 styled. 来定义自定义组件,而进行样式扩展的话,我们可以使用 styled(xx)

我们以 DefaultButton 为例,想要在 DefaultButton 样式的基础上做额外的扩展,我们可以通过 styled(DefaultButton) 来重新定义一个新的组件,并且在实现过程中,它拥有除了 DefaultButton 之外的样式逻辑。

ExtendedButton定义

export const ExtendedButton = styled(DefaultButton)`
display: block;
width: 100vw;

App.js

import H1 from "./components/Title";
import { DefaultButton,ExtendedButton } from "./components/Buttons";

function App({
  return (
   <div>
      <H1>Front789H1>
      <p>专注于前端开发技术/Rust及AI应用知识分享的Coderp>
      <DefaultButton>我是按钮!DefaultButton>
      <ExtendedButton red>扩展按钮!ExtendedButton>
  div>

  );
}

export default App;

这样,我们就可以拥有一个在 DefaultButton 样式基础上,扩展了 width:100vw 的新组件。


5. 嵌套样式

当然,现在的前端样式已经不满足之前介绍的针对单个元素的样式封装。我们还可以拥有像 less/scss 一样的样式嵌套。这样我们就可以在一个样式逻辑种处理其内部的多个子元素。实现更好的封装。

import styled from 'styled-components';

const Wrapper = styled.div`
h1{
  text-align: center;
  color: violet;
}

p{
  font-size: 40px;
}

button{
  background-color: pink;
  padding: 4px 8px;
  border: none;  
}
`

function App({
  return (
    <div>
      <Wrapper>
        <h1>Front789h1>
        <p>专注于前端开发技术/Rust及AI应用知识分享的Coderp>
        <button>我是按钮!button>
      Wrapper>
  div>

  );
}

export default App;

当我们在页面中使用了 Wrapper 后,内部的所有

标签都将具有 40px 的字体大小, 将具有粉色的背景颜色、指定的填充和无边框。


6. 扩展 React 组件

我们使用 styled components 还可以处理用常规方式构建的 React 组件。此时,我们只需要将之前的组件放到 styled(xx) 中即可。(需要做一点小的变更)

假如我们有如下的 react 组件

const




    
 Oldcom = () => {
  return (
    <div>
        <h2>Front789h2>
        <button>按钮!button>
    div>

  )
}

如果我们想通过 styled components 对其处理,我们需要对其做一下改造。需要在 props 中接受 className ,并且讲其放置到组件的根元素上,然后就可以利用 styled components 嵌套样式对其内部的元素进行样式处理。

import React from 'react'
import styled from 'styled-components'

export const Oldcom = ({className}) => {
  return (
    <div className={className}>
        <h2>Front789h2>
        <button>按钮!button>
    div>

  )
}

export const NewCom = styled(Oldcom)`
h2{
    color: green;
    text-align: center;
}

button{
    padding: 4px 10px;
    background-color: violet;
    border: none;
}
`



7. CSS变量

使用 styled components 构建的组件,还支持使用 css变量 。这样,我们在组件内部接收一些团队定义的变量,来处理指定的样式逻辑。

让我们来看看它是如何实现的。

现在在 src 文件夹中创建一个 index.css 文件,该文件中编写一些 CSS 变量,这些变量是从任何地方都可以访问的 「全局样式」

index.css

:root{
--primary-color#8F00FF
}

现在 :root{} 就像在 CSS 中选择 html{} 一样。但是 :root{} 的优先级比 html{} 更高。

CSS有两种方式来选择HTML文档的根元素

  1. :root 伪类
  2. html 选择器

选择器的特异性

:root 选择器的优先级高于 html 选择器。这是因为 :root 是一个 伪类选择器 ,而 html 是一个 类型选择器

:root {
 background-color: red;
}
html {
 background-color: blue;
}
/* HTML 文档的根元素将具有红色的背景颜色。 */

目标化根元素

除了 HTML 外, CSS 还可以用于样式化其他类型的文档。这就是 :root 元素发挥作用的地方,它允许你样式化文档的根元素。当样式化 SVG 文档时,这可能特别重要,因为 html 选择器不起作用。

然后,我们可以在 styled components 定义的组件种使用这个 css变量 。(当然,别忘了在 index.js 中导入 index.css

const Newcom = styled(Oldcom)`
h2{
    color: green;
    text-align: center;
}

button{
    padding: 4px 10px;
    background-color: var(--primary-color);
    border: none;
}

8. 添加主题

有些网站还需要一些 明暗主题 的切换。使用 styled components 可以轻松实现这一点。

  1. 首先,我们需要从 styled components 中导入 ThemeProvider
  2. 然后将整个应用程序包装在 ThemeProvider 中,并在其中提供我们的主题。
  3. 使用 styled component 定义一个组件( Container ),在其内部可以访问主题及其属性,并帮助用户更改背景颜色和文本颜色
  4. 我们可以定义一个操作(按钮点击)来更换 theme 变量 具体实现代码如下:
import styled, { ThemeProvider } from 'styled-components'
import React, { useState } from 'react'
import {DefaultButton } from './components/Buttons'

const lightTheme = {
  background'#fff',
  color'#222',
}
const darkTheme = {
  background'#222',
  color'#fff',
}

const Container = styled.div`
color: ${(props) => props.theme.color};
background-color: ${(props) => props.theme.background};
height: 100vh;
`

function App({
  const [theme,setTheme] = useState(lightTheme)
  return (
    <ThemeProvider theme={theme}>
      <DefaultButton onClick={() => 
        setTheme(theme === lightTheme ? darkTheme : lightTheme)
      }>切换主题DefaultButton>
      <Container>
        <p>专注于前端开发技术/Rust及AI应用知识分享的Coderp>
      Container>
    ThemeProvider>

  );
}

export default App;


9. 处理动画

styled components 还支持动画,我们可以从 styled-components 中导入 keyframes ,用它来创建动画。

import styled, {keyframes} from 'styled-components'

const spinner = keyframes`
to{
 transform: rotate(360deg);
}
`


const Loading = styled.div`
width: 6rem;
height: 6rem;
border: 5px solid #ccc;
border-radius: 50%;
border-top-color: black;
animation: ${spinner} 0.6s linear infinite;
`


export default Loading

在上面的代码中,我们定义了 spinner 动画, spinner 是动画的名称。在 Loading 中,我们使用了 spinner 动画名称,以便 Loading 使用该动画。








请到「今天看啥」查看全文