组件化下的CSS
现在整个前端都已经是组件化的了,而CSS的设计就不是为组件化而生的,所以目前在组件化的框架中,都需要一种合适的CSS解决方法
在组件化中选择合适的CSS解决方案应该满足以下条件:
- 可以编写局部的CSS:CSS具备自己的作用域,不会随意污染其他组件内的元素
- 可以编写动态CSS:可以获取当前组件的一些状态,根据状态的变化生成不同的CSS样式
- 支持所有的css特性:伪类、动画、媒体查询等
- 编写起来简单方便、最好符合一贯的CSS风格特点
React中的CSS
CSS一直是React的痛点,这这一点上,Vue做的要好于React
- Vue通过在.vue文件中编写<style></style>标签来编写自己的样式
- 通过是否添加scoped属性来决定编写的样式是全局有效还是局部有效
- 通过lang属性来设置less、sass等预处理器
- 通过内联样式风格的方式来根据最新状态设置和改变css
Vue的解决方案虽然不能称为完美,但是已经足够简洁、方便、自然,至少统一的样式风格不会出现多个开发人员、多个项目采用不一样的样式风格。
相比而言,React官方并没有给出在React中统一的样式风格,由此,从普通的css到css modules,再到css in js,有几十种不同的解决方案,上百个不同的库
内联样式
内联样式是官方推荐的一种css样式
- style接收一个采用小驼峰命名属性的JavaScript对象,而不是CSS字符串
- 并且可以引用state中的状态来设置相关的样式
内联样式的有点:
- 内联样式,样式之间不会有冲突
- 可以动态获取当前state中的状态
内联样式的缺点
- 写法上都需要使用驼峰标识
- 某些样式没有提示
- 大量的样式,代码比较混乱
- 某些样式无法编写(比如伪类、伪元素)
官方已让是希望内样式和普通CSS结合来编写
import React, { PureComponent } from 'react'
export default class App extends PureComponent {
constructor(props) {
super(props);
this.state = {
color: "purple"
}
}
render() {
const pStyle = {
color: this.state.color,
textDecoration: "underline"
}
return (
<div>
<h2 style={{fontSize: "50px", color: "red"}}>我是标题</h2>
<p style={pStyle}>我是一段文字描述</p>
</div>
)
}
}
普通的CSS
普通的css我们通常会编写到一个单独的文件,之后再进行引入
App.js中编写React逻辑代码
import React, { PureComponent } from 'react';
import Home from './Home';
import './App.css';
export default class App extends PureComponent {
render() {
return (
<div className="app">
<h2 className="title">我是App的标题</h2>
<p className="desc">我是App中的一段文字描述</p>
<Home/>
</div>
)
}
}
App.css中编写React样式代码
.title {
color: red;
font-size: 20px;
}
.desc {
color: green;
text-decoration: underline;
}
这样的编写方式和普通的网页开发中编写方式是一致的
- 如果我们按照普通的网页标准去编写,那么也不会有太大的问题
- 但是组件化开发中,我们总是希望组件是一个独立的木块,即使是样式也只是在我们内部生效,不会互相影响
- 但是普通的css都是全局的css,样式之间会互相影响
css modules
css modules并不是React独有的解决方案,而是所有使用了类似于webpack配置的环境下都可以使用的,但是如果在其他项目中使用这个,那么就需要我们自己来进行配置,比如配置webpack.config.js中的modules:true
等
React的脚手架已经内置的了css modules的配置:.css/.less/.scss等样式文件都被修改成.module.css/.module.less/.module.scss等,之后就可以引用并且直接使用了
css modules确实解决了局部作用域的问题,也是很多人喜欢在React中使用的一种方法
但是这种方案也有自己的缺陷
- 引用的类名,不能使用连接符(.home-title),在JavaScript中是不识别的
- 所有className都必须使用{style.className}的形式来编写
- 不方便动态来修改某些样式,依然需要使用内联样式的方式
CSS in JS
官方文档中也有提到过CSS in JS这种方案
CSS-in-JS是指一种模式,其中CSS由JavaScript生成而不是在外部文件中定义,此功能并不是React的已不是,而是由第三方库提供。React对样式如何定义没有明确态度。
在传统的前端开发中,我们通常会将HTML,CSS,JavaScript进行分离,但是在React的思想中,认为逻辑本身和UI是无法分离的,所以才有了JSX语法。样式也是属于UI的一部分,事实上CSS-in-JS的模式就是一种将样式也写入到JavaScript中的方式,并且可以方便的使用JavaScript的状态,所以React有人称之为All in JS
认识styled-components
CSS-in-JS通过JavaScript来为CSS赋予一些能力,包括类似于CSS预处理器一样的样式嵌套、函数定义、逻辑复用、动态修改状态等。
CSS预处理器也具备某些能力,但是获取动态状态依然是一个不好处理的点
所以目前可以说CSS-in-JS是React编写CSS最为手欢迎的一中解决方案
目前比较流行的CSS-in-JS的库
- styled-components
- emotion
- glamorous
目前可以说styled-components依然是社区最流行的CSS-in-JS库
yarn add styled-components
ES6标签模板
ES6中增加了模板字符串的语法,模板字符串的的另一种用法是标签模板字符串(Tagged Template Literals)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 1.模板字符串的基本使用
const name = "why";
const age = 18;
const message = `my name is ${name}`;
// 2.标签模板字符串: 可以通过模板字符串的方式对一个函数进行调用
function test(...args) {
console.log(args);
}
// test("aaa", "ccc");
// test`aaaa`;
test`my name is ${name}, age is ${age}`;
test`
font-size: 15px;
color: red;
`
</script>
</body>
</html>
正常情况下,我们同时通过 函数名()的方式来进行的调用的,其实函数还有另一种调用方式 方法名``
如果我们在调用的时候插入了其他的变量
- 模板字符串被拆分了
- 第一个元素是数组,是被模块字符串拆分的字符串组合
- 后面的一个元素是一个个模块字符串传入的内容
styled Components中,就是通过这种方式来解析模块字符串,最终生成我们想要的的样式
styled的基本使用
styled-components的本质是通过函数的调用,最终创建出一个组件
- 这个组件会被自动添加上一个不重复的class
- style-components会给该class添加相关的样式
另外,它支持类似于CSS预处理器一样的样式嵌套
- 支持直接子代选择器或者后代选择器,并且直接编写样式
- 可以通过&顾浩获取当前元素
- 直接伪类选择器、伪元素等
import styled from 'styled-components';
export const HomeWrapper = styled.div`
font-size: 12px;
color: red;
.banner {
background-color: blue;
span {
color: #fff;
&.active {
color: red;
}
&:hover {
color: green;
}
&::after {
content: "aaa"
}
}
/* .active {
color: #f00;
} */
}
`
export const TitleWrapper = styled.h2`
text-decoration: underline;
color: ${props => props.theme.themeColor};
font-size: ${props => props.theme.fontSize};
`
import React, { PureComponent } from 'react';
import {
HomeWrapper,
TitleWrapper
} from "./style";
export default class Home extends PureComponent {
render() {
return (
<HomeWrapper>
<TitleWrapper>我是home的标题</TitleWrapper>
<div className="banner">
<span>轮播图1</span>
<span className="active">轮播图2</span>
<span>轮播图3</span>
<span>轮播图4</span>
</div>
</HomeWrapper>
)
}
}
props、attrs属性
props可以穿透
props可以传递给styled组件
- 获取props需要通过${}传入一个插值函数,props会作为该函数的参数
- 这种方式可以有效的解决动态样式的问题
- 添加attrs属性
import React, { PureComponent } from 'react';
import styled from 'styled-components';
/**
* 特点:
* 1.props穿透
* 2.attrs的使用
* 3.传入state作为props属性
*/
const HYInput = styled.input.attrs({
placeholder: "coderwhy",
bColor: "red"
})`
background-color: lightblue;
border-color: ${props => props.bColor};
color: ${props => props.color};
`
export default class Profile extends PureComponent {
constructor(props) {
super(props);
this.state = {
color: "purple"
}
}
render() {
return (
<div>
<input type="password"/>
<HYInput type="password" color={this.state.color}/>
<h2>我是Profile的标题</h2>
<ul>
<li>设置列表1</li>
<li>设置列表2</li>
<li>设置列表3</li>
</ul>
</div>
)
}
}
styled高级特性
支持样式继承
const Button = styled.button`
padding: 8px 30px;
border-radius: 5 px;
`
const WarnButton = styled(Button)`
background-color: red;
color: #fff;
`
设置主题
import {ThemeProvider} from 'styled-components';
<ThemeProvider theme={{color: "red", fontSize: "30px"}}>
<Home/>
<Profile/>
</ThemeProvider>
const ProfileWrapper = styled.div`
color: ${props => props.theme.color};
font-size: ${props => props.theme.fontSize};
`
vue中添加class
vue中添加class可以传入一个对象
<div class="static"
v-bind: class="{active: isActive, 'text-danger': hasError}">
</div>
也可以传入一个数组
<div v-bind:class="[activeClass, errorClass]">
</div>
也可以是对象和数组的混合
<div v-bind:class="[{active: isActive}, errorClass]">
</div>
React中添加class
JSX
<div>
<h2 className={"title " + (isActive? "active":"")}>我是标题</h2>
<h2 className={["title",(isActive? "active":"")].join()}>我是标题</h2>
</div>
也可以借助第三方库classnames
classNames('foo','bar'); //=> 'foo bar'
classNames('foo',{bar: true}); //=> 'foo bar'
classNames({'foo-bar':true}); //=> 'foo bar'
classNames({'foo-bar':false}); //=> ''
classNames({foo:true},{bar:true}); //=> 'foo bar'
classNames({foo:true,bar:true}); //=> 'foo bar'
classNames('foo',{bar:true,duck:false},'baz',{quux:true}); //=> 'foo bar baz quux'
classNames(null, false,'bar',undefined,0,1,{baz:null},''); //=> 'bar 1'
import classNames from 'classnames';
<div>
<h2 className={classNames('foo','bar')}>我是标题</h2>
</div>