掌握React(三)-- 组件样式

admin 2023-05-13 07:44:03 2006

我们已学习了组件的创建,本文介绍组件的样式(CSS),您将了解到Vanilla CSS(明确手写的CSS)、CSS模块、CSS-in-JS、CCS库的使用等知识。

 


 

Vanilla CSS

现在已经有了非常好用的CSS库,比如Bootstrap、Material UI,所以手动编写Vanilla CSS的方法又浪费时间又乏味,已很少人使用了。我们可以了解下怎么做。

 

先在main.tsx文件中,删除从bootstratp导入css的语句,然后在components文件夹下新建ListGroup.css文件,再新建ListGroup文件夹,然后将ListGroup.css和ListGroup.tsx两个文件移至ListGroup文件夹中。再编写ListGroup.css文件:

.list-group{
    list-style: none;
    padding: 0;
}

在ListGroup.tsx文件顶部导入ListGrooup.css:

import "./ListGroup.css";

保存文件,发现列表框样式已变了。另外,在App.tsx文件中,由于ListGroup.tsx放在了ListGroup文件夹下,所以导入语句要加一层ListGroup.

import ListGroup from "./components/ListGroup/ListGroup";

顺便说一下,这个导入语句有点嵌套难看,我们可以在ListGroup文件夹下新建一个index.ts文件:

import ListGroup from "./ListGroup";
export default ListGroup;

然后再在App.tsx顶部导入ListGroup只需定位到文件夹。React会查找该文件夹的index.ts文件,如果index.ts文件收集并export了组件,就可以直接使用。

import ListGroup from "./components/ListGroup";

CSS模块

Vanilla CSS有个问题,就是容易被其他同名的CSS的覆盖。比如我们在App.css里添加同名 list-group:

.list-group{
    background: red;
}

然后在App.tsx项部导入App.css:

import "./App.css";

查看网页,背景已变红了,说明与App.css的同名 list-group的css冲突了。我们可以用CSS模块来解决这个问题,CSS模块限定了CSS的范围,类似javascript的模块,这样就不必担心同名CSS的冲突问题了。下面就来介绍下具体怎么做。

 

先将ListGroup.css改名为ListGroup.module.css

 

再在ListGroup.tsx导入,导入语句稍有些不同,与javascript模块一样。另外,调用时由于有连字符 - ,不能使用点. 操作符styles.list-group,要用方括号。

import styles from "./ListGroup.module.css";
...
      <ul className={styles["list-group"]}>
      ...

如果觉得这样比较丑,可以改一下class名,驼峰命名法命名为listGroup,然后再用点.操作。

.listGroup{
    list-style: none;
    padding: 0;
}
<ul className={styles.ListGroup}>

如果某个元素需要应用多个css的class,比如我们还要应用 container的css类.

ListGroup.module.css:

.listGroup{
    list-style: none;
    padding: 0;
}

.container{
    background: yellow;
}

再在ListGroup.tsx使用数组,并用join方法合并。

...
      <ul className={[styles.ListGroup, styles.container].join(" ")}>
      ...

CSS-IN-JS

样式组件的另一种技术是CSS-IN-JS,该技术目前还有争议,有人喜欢有人讨厌,它是把CSS与组件放在一起。有一些优点,首先是范围限制不会与其他CSS发生冲突,再是CSS和JS或TS代码放在一起容易干净删除一个组件,还有就是更容易样式化基于Props/state的组件。

CSS-IN- JS
Scoped styles
All the CsS & JS/TS code in one place
Easier to delete a component
Easier to style based on props state

该技术有很多库可以使用,使用较多的是以下三种。

LIBRARIES
Styled components
Emotion
Polished

这一节就来了解下styled components。

 

先来安装styled-components,可能是版本问题,目前在Typescript中import该库,都有警告提示找不到,我们需要再安装一个库 @types/styled-components,希望以后的版本不再有这个问题。

npm i styled-components
npm i @types/styled-components

现在就可以使用styled了,它的属性包括了html各个元素。在ListGroup组件中,我们有ul和li两种html元素,可以用styled.ul`...`和styled.li`...`定义相应的css,并返回一个react组件,这里我们命名为List和ListItem。然后我们在ListGroup组件中就可以使用这两个组件了,分别代替ul和li,同时也去掉 className的css标记。

...
import styled from "styled-components";

const List = styled.ul`
  list-style: none;
  padding: 0;
`;
const ListItem = styled.li`
  padding: 5px 0;
`;
...
function ListGroup({ items, heading, onSelectItem }: Props) {
  ...
      <List>
        ...
          <ListItem
            ...
          >
            ...
          </ListItem>
        ))}
      </List>
    </>
  );
}

我们看上面的jsx语句,没有对外的css的依赖,全部是组件。一个文件包含了所有内容。后面如果需要删除该组件的话,直接删就好,不会对其他部分产生影响。

 

前面我们说过CSS-IN-JS的优点之一是方便使用基于props/state的组件,现在就来看看怎么用。

 

添加一个props,有一个类型为boolean的属性active,然后组件ListItem的styled.li传入该props,active为true时,背景为蓝色blue。最后ListGroup使用ListItem时,传入active={index===selectedIndex}。这样就能实现当点击某一个列表项时,背景高亮显示蓝色。

...
interface ListItemProps {
  active: Boolean;
}
const ListItem = styled.li<ListItemProps>`
  padding: 5px 0;
  background: ${(props) => (props.active ? "blue" : "none")};
`;

...
function ListGroup...
          <ListItem
            active={index === selectedIndex}
            ...
          </ListItem>
    ...

关注点分离

软件工程一个重要原则是关注点分离,该原则建议将各功能模块放在不同部分,这样会比将所有功放在一起更好。使用关注点分离,我们的应用会变成模块化,更容易理解、维护、修改。

 

模块化带来一些好处,我们可以独立地构建、测试各模块,并可在其他应用中重用模块,每一个模块只负责一个关注点。

这有点像饭店的工作,厨师只负责烧菜,服务员只负责服务顾客,各司其职。

 

模块里的复杂实现隐藏在一个定义好的接口后面,类似电视机的遥控器有很多复杂的实现,但它的按钮也就这么几个。隐藏了细节,其他应用也就很容易使用。

有人说,CSS-IN-JS违反了关注点分离原则,因为我们把所有东西放在了一起。

实际上,关注点分离的原则不是说将所有文件分离或放在一起,而是强调应用里的每一个模块都应该完成一个特定的功能,而围绕这一功能的复杂细节应该隐藏在定义好的接口里。

在我们的ListGroup里,实现细节都被隐藏,而props的heading、items相当于该模块的接口。使用ListGroup的用户可不关心ListGroup的实现细节,不关心它是放在一个文件或多个文件里,所以CSS-IN-JS 并不违反关注点分离的原则。

当然,您可以有不同意见,哈~

内联样式

可以直接在html元素中使用 style 属性设置css。style用大括号包起来。与普通的css区别在于,css的class命名方法不一样,不用连字符 - ,而是用驼峰命名法,比如用 backgroundColor,而不是background-color,这是一个不好的实践,除非是没得选择,一般不使用内联样式。

 

<ul className="list-group" style={{ backgroundColor: "yellow" }}>

流行的UI库

目前有很多UI库用来创建现代漂亮的网站,比如Bootstrap, Material UI, Tailwind CSS等,前面我们已用过Bootstrap了,它提供给我们非常有用且易用的组件。

 

Material UI是一个开源的React组件库,它实现了google material design,后者是一种设计语言,用在Google各种产品中,我们可以使用它快速实现相关UI而不用手写CSS样式。

 

Tailwind稍有些不同,它提供了一些实用的小的CSS类,比如 list-none就表示是{list-style:none}; 而padding为0,则可以使用p-0的class. 使用Wailwind, 我们可以不用再手写css了而直接使用这些简单实用的class。

但随着UI越来越复杂,使用Tailwind最终可能需要标记很多 class,标记会很长。

 

<div class='flex w-full flex-col items -center gap-4' >
...
</div>

如果这不是你想要的,可以使用关联的css库,Daisy UI 或Chakra UI。

Daisy UI组合了标记,看上去更清晰更短一些,有点类似bootstrap.

<div class='alert alert-success'>
...
</div>

Chakra UI类似于Material UI,它是React组件,基于Tailwind构建。

 


 

添加图标

本节介绍react添加图标,用到一个react库 react-icons。先安装。我们使用版本4.7.1。

npm i [email protected]

谷歌搜索 "react icons"找到官网,搜索某个图标比如"calendar",能看到有多种风格的图标,并以具体风格的前缀命名图标,我们点击某个图标就复制了该图标名称,比如BsFillCalendarFill,然后回到App.tsx编写。

 

在顶部导入 BsFillCalendarFill 图标组件,由于是bs开头,我们加上/bs, react-icons/bs。然后可以像使用普通组件一样使用图标,添加属性,这里添加了color, size等。

import { BsFillCalendarFill } from "react-icons/bs";
function App() {
  return (
    <div>
      <BsFillCalendarFill color="red" size="40" />
    </div>
  );
}

export default App;

运行效果是这样的

练习一:使用CSS模块


前面我们创建了一个Button组件,后来我们去掉了引入bootstrap的css的语句,目前显示的Button非常丑,我们来练习下如何使用CSS模块来美化该按钮。

 

解答:

在components下新建文件夹Button,将Button.tsx移入其中,再添加文件Button.module.css,键入css,当然配色及设计是完全主观的。我们可以再增加比如 btn-secondary, btn-danger等class,由于是重复的,这里就不添加了。

.btn{
    padding: 8px 12px;
    border-radius: 3px;
    border: 0;
}

.btn-primary{
    background-color: #0d6efd;
    color: white;
}

然后到Button.tsx中调用即可。

import styles from "./Button.module.css";
const Button = (...
        className={[styles.btn, styles["btn-" + color]].join(" ")}
       ...

练习二:创建Like组件

创建一个可重用的Like组件,点击它就简单在控制台显示clicked信息。

 

解答:

 

在components文件夹下新建Like.tsx文件。导入react-icons的AiFillHeart和AiOutlineHeart组件,再设置state表示是否点中,然后创建一个接口onClick可以让调用它的父组件处理事件。

import { useState } from "react";
import { AiFillHeart, AiOutlineHeart } from "react-icons/ai";

interface Props {
  onClick: () => void;
}

const Like = ({ onClick }: Props) => {
  const [status, setStatus] = useState(false);
  const toggle = () => {
    setStatus(!status);
    onClick();
  };
  if (status)
    return (
      <AiFillHeart color="#ff6b81" size={40} onClick={toggle}></AiFillHeart>
    );
  return <AiOutlineHeart size={40} onClick={toggle}></AiOutlineHeart>;
};

export default Like;

这里是App组件调用,App.tsx:

import Like from "./Like";
function App() {
  return (
    <div>
      <Like onClick={() => console.log("clicked")} />
    </div>
  );
}
export default App;

点击Like组件的效果变化如下。

小结

 

本文介绍了React组件的样式,我们可以根据需要选择某种技术实现样式。

下一篇计划:管理组件状态。

可爱猫?Telegram电报群 https://t.me/ikeaimao

社区声明 1、本站提供的一切软件、教程和内容信息仅限用于学习和研究目的
2、本站资源为用户分享,如有侵权请邮件与我们联系处理敬请谅解!
3、本站信息来自网络,版权争议与本站无关。您必须在下载后的24小时之内,从您的电脑或手机中彻底删除上述内容
最新回复 (0)

您可以在 登录 or 注册 后,对此帖发表评论!

返回