项目实战
使用CSS
尽量不要使用内联CSS
- 内联style代码多,性能差,扩展性差
- 外链css文件可复用代码,可单独缓存文件
元素内联style
- 和HTMl元素的style相似
- 必须用JS写法,不能是字符串,里面必须是对象
<span style={{ color: "green" }}>已发布</span>
- 驼峰写法
使用css文件
- 引入css文件
- JSX中使用className
<div key={id} className="list-item">
let itemClassName = "list-item";if (isPublished) itemClassName += " published";<div key={id} className={itemClassName}>
- 可使用clsx或classnames做条件判断,两个功能相近,都是判断class条件的集合,比如当判断的条件多了,使用上面的if无法满足的时候就需要借用到工具。在这个项目中用classnames做例子,通过下列步骤使用
- classnames仓库地址
下载指令
npm install classnames
import classnames from "classnames";const itemClassName = classnames("list-item", { published: isPublished });// 上下两种含义一致,只是不同的写法const itemClassName = classnames({"list-item": true,published: isPublished,});
CSS Module
普通CSS的问题,React使用组件化开发,多个组件就需要多个CSS文件,多个CSS文件很容易造成className重复。在没有相应的工具前使用的是BEM,一种软性规范。主观性过强而不推荐。以下是CSS module的特点
- 每个CSS文件都当作单独的模块,命令xxx.module.css
- 为每个className增加后缀名,不重复
- Create- React-App原生支持CSS Module
将文件名更改为xxx.module.css的格式,样式的格式不变
// QuestionCard.module.css 文件中
.list-item{border: 1px solid black;padding: 10px;margin-bottom: 16px;
}
.published{border: 1px solid greenyellow;
}
引入和使用,这一部分和原来的差别比较大
// import classnames from "classnames";
// 上面是原来的,下面是module的引入
import styles from "./QuestionCard.module.css";// 使用格式,其中list-item因为有-符号,如果直接styles.list-item会报错,下面这个js写法就可以<div key={id} className={styles["list-item"]}>
Sass
CSS语法比较原始,不能嵌套。现代开发一般使用less sass等预处理语言。CRA原生支持Sass Module,后缀直接改为.scss即可
下载指令
npm install sass --save
接下来将想要使用的文件格式改为xxx.scss,记得后缀为scss。
import styles from "./QuestionCard.module.scss";const itemClassName = classnames({[styles["list-item"]]: true,[styles["published"]]: isPublished,});<div key={id} className={itemClassName}>
其中对于itemClassName的中括号的解释如下,在这段代码中,中括号([]
)用于在JavaScript对象字面量中动态地设置属性名。这种语法是ES6)中引入的计算属性名的一个特性,其键为变量y而不是固定字符比如a时,这个写法实际上是将这个变量的引用值传递进去
CSS-in-JS
- 一种解决方案(而非工具名称),有好几个工具
- 在JS中写CSS,带来极大的灵活性
- 它和内联style完全不一样,也不会有内联style的问题和className的问题(会自行生成class)
Style-components
官网 可能是外网的链接,打开的时候速度有点慢
下载指令
npm install styled-components
引入代码如下,视频中老师的引入爆红线,需要额外下载东西,我这边没有,不过也顺便下载了。下面这个组件可以测试引入是否成功
下载指令
npm i --save-dev @types/styled-components
import React, { FC } from "react";
import styled, { css } from "styled-components";const Button = styled.button<{ $primary?: boolean }>`background: transparent;border-radius: 3px;border: 2px solid #bf4f74;color: "#BF4F74";margin: 0 1em;padding: 0.25em 1em;${(props) =>props.$primary &&css`background: blue;color: white;`};
`;const Container = styled.div`text-align: center;
`;const Demo: FC = () => {return (<div><p>styled-components demo</p><Container><Button>normal button</Button><Button $primary>primary 按钮</Button></Container></div>);
};
export default Demo;
大概解释一下上述代码中的逻辑,其中styled可以理解为一个类,而styled.button和styled.div小数点后的两个属性 都可以理解为方法,包括css后面加字符串,css也是个函数,反引号可以理解为传参如下图所示,多一层括号显得麻烦,所以不用括号的形式
styled-jsx
仓库地址
项目中不使用这个,因为ts环境中对标签的属性比较敏感,而这个工具插入了一些非标准的属性,导致需要额外扩展比较多的功能,用于js没问题,可以选择性的使用。
emotion
官网地址
使用起来的形式和前面的components比较类似,但是同样有个问题,就是这个工具在标签中添加了css属性,在ts中这么设置会报错,所以ts环境中同样不进行使用.
重构列表页,增加css样式
选择CSS-Module
- 简单易用,学习成本较低
- 性能更好,使用CSS-in-JS会增加编译时间
- 不需要灵活变换样式
新建React项目,具体过程参考这一章,原来有很多个Demo的也进行保留,两个都会讲。以下几个文件直接复制粘贴可以用
App.tsx
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import List from "./pages/list";function App() {return (<div><h1>问卷F1</h1><List /></div>);
}export default App;
list.tsx
import React, { FC, useState } from "react";
import QuestionCard from "../components/QuestionCard";
import styled from "./list.module.scss";const rawQuestionList = [{_id: "q1",title: "问卷1",isPublished: true,isStar: false,answerCount: 5,createAt: "3月10日 13:23",},{_id: "q2",title: "问卷2",isPublished: false,isStar: true,answerCount: 15,createAt: "3月22日 13:23",},{_id: "q3",title: "问卷3",isPublished: true,isStar: true,answerCount: 100,createAt: "4月10日 13:23",},{_id: "q4",title: "问卷4",isPublished: false,isStar: false,answerCount: 98,createAt: "3月23日 13:23",},
];const List: FC = () => {const [questionList, setQuestionList] = useState(rawQuestionList);return (<><div className={styled.header}><div className={styled.left}><h3>我的问卷</h3></div><div className={styled.right}>搜索</div></div><div className={styled.content}>{questionList.map((q) => {const { _id } = q;return <QuestionCard key={_id} {...q} />;})}</div><div className={styled.footer}>footer</div></>);
};export default List;
list.module.scss
.header{display: flex;.left{flex: 1;}.right{flex: 1;text-align: right;}
}.content{margin-bottom: 20px;
}.footer{text-align: center;
}body{background-color: #f1f1f1;
}
QuestionCard.module,scss
.container{margin-bottom: 20px;padding: 12px;border-radius: 3px;background-color: white;&:hover{box-shadow: 0 4px 10px lightgray;}
}.title{display: flex;.left{flex: 1;}.right{flex: 1;text-align: right;}
}.button-container{display: flex;.left{flex: 1;}.right{flex: 1;text-align: right;button{color: #999;}}
}
QuestionCard.tsx
import React, { FC, useEffect } from "react";
// import "./QuestionCard.css";
import styled from "./QuestionCard.module.scss";
import classnames from "classnames";
type PropsType = {_id: string;title: string;isPublished: boolean;isStar: boolean;answerCount: number;createAt: string;// 问号是可写可不写,跟flutter语法相似deletQuestion?: (id: string) => void;pubQuestion?: (id: string) => void;
};const QuestionCard: FC<PropsType> = (props: PropsType) => {const { _id, title, createAt, answerCount, isPublished } = props;return (<div className={styled.container}><div className={styled.title}><div className={styled.left}><a href="#">{title}</a></div><div className={styled.right}>{isPublished ? (<span style={{ color: "green" }}>已发布</span>) : (<span>未发布</span>)} <span>答卷:{answerCount}</span> <span>{createAt}</span></div></div><div className={styled["button-container"]}><div className={styled.left}><button>编辑问卷</button><button>数据统计</button></div><div className={styled.right}><button>标星</button><button>复制</button><button>删除</button></div></div></div>);
};export default QuestionCard;