在目前整个前端都使用组件化开发的模式下,CSS样式的编写就成为了一个问题。因为CSS也叫做层叠样式表,意思就是多个css样式作用于同一个HTML元素的时候,浏览器会根据权重的大小来进行覆盖,为元素应用权重最高的那一组css样式,很明显这种特性不适合组件化开发。
相比于React,同为前端框架的Vue在css样式编写上要做的比React好,比如:
一般实现样式动态变化的方案:
React官方一致没有给出在React中统一的风格样式,普通的css,css modules以及css in js,很多种方案带来了上百种不同的库,到目前为止没有统一的方案。
React官方推荐我们使用style标签内联样式这种写法来进行组件样式的编写,规定style标签接收一个采用小驼峰命名属性的js对象,而不是css字符串。通过这种方式写的样式会将样式添加到元素的内联样式上。
优点:
基于内联样式书写的样式肯定不会导致样式冲突
可以动态获取state中的状态来完成动态样式
缺点:
采用小驼峰写法,有的css书写没有提示易错
在JSX中写大量的style样式,比较混乱
伪类,伪元素这种样式无法通过内联样式编写
class App extends PureComponent{
constructor(props) {
super(props);
/* 动态改变元素样式 */
this.state = {
textColor:"pink"
}
}
render(){
/* 将样式抽取到一个变量中 */
const h2Style={
fontSize:"18px",
color:"red"
}
return(
<div>
<h2 style={h2Style}>这是一个App组件</h2>
<p style={{fontSize:"18px",color:"red"}}>这是一段文字</p>
<div style={{color:this.state.textColor}}>这是一段动态变化的文字</div>
</div>
)
}
}
这种方案和传统的在网页中进行开发时的编写方式是一致的,传统的网页开发编写css的优点它有,对应的缺点它也同样存在。
通常是新建一个和组件一一对应的.css文件,然后给组件最外层的div元素一个className。在.css文件中编写对应的样式文件,然后在组件.js文件中导入该样式文件即可将样式应用到组件中对应的元素标签上。
优点
编写规范简单,不需要用小驼峰这种不熟悉的语法去写
缺点(主要就是样式的层叠覆盖,这种方法写的css都是全局作用域的)
每次都要在最外层增加一个className,避免样式冲突
每次在编写样式都要先写一个.className
就算写了还是有可能会冲突,比如其他组件中有权重更高的选择器
使用直接子代选择器可以避免 但是复杂度就太麻烦了
.app .title{
font-size: 32px;
color: red;
font-weight: bold;
}
import "./index.css"
class App extends PureComponent{
render(){
return(
<div className="app">
<h2 className="title">这是一个App组件</h2>
</div>
)
}
}
css modules是一种在使用了类似于webpack配置的开发环境下都可以使用的css解决方案,主要用于解决相互独立的组件的样式互相冲突和覆盖的问题。
在Vue项目中,我们需要自己手动在webpack.config.js中进行配置;而React中基于脚手架搭建的项目已经帮助我们内置了css modules的配置。
使用方法(假设为Home组件添加css样式)
.title{
font-size: 32px;
color: red;
font-weight: bold;
}
.banner {
font-size: 28px;
color:pink;
font-weight: bold;
}
import appStyles from "./index.module.css";
class App extends PureComponent{
render(){
const {title,banner} = appStyles; /* 解构赋值*/
return(
<div>
<h2 className={title}>这是一个title</h2>
<p className={banner}>这是一个banner</p>
</div>
)
}
}
使用原理
打印appStyles对象,如下:
每一个类名都会当作一个属性,属性值为"当前css文件所在文件名_类名_随机唯一值"
类名唯一,所以样式不会冲突,也是唯一的。
如果文件名为index.module.css,那么第一个值是该文件所在的文件夹名称,如"React中的样式方案";
如果文件名不是以index开头的,那么第一个值是该文件本身的名称,如home
{
banner: "React中的样式方案_banner__klcc5"
title: "React中的样式方案_title__26xd3"
banner: "home_banner__klcc5"
}
优点
解决了css中样式冲突的问题,等于让每一个组件中的css样式都有了自己组件作用域
缺点
CSS in JS在React的官方文档上描述为:CSS in JS是一种模式,指的将CSS样式由js生成而不是在外部的样式文件中定义,这个功能不由React提供,需要由第三方库来提供。
在传统的网页开发中提倡结构样式行为相分离,但是React的思想中认为逻辑(js)本身和UI是无法完全分离的,所以才有了JSX语法,一种将逻辑和结构相互结合嵌套的写法。而CSS-in-JS的模式就是将样式CSS代码也写入到js中的方式,并且这种模式的优势在于CSS可以轻松的使用JS中的state状态,正因为此,React才被人们称之为All in JS。
CSS in JS基于JS提供给CSS的能力,可以实现类似于CSS预处理器的大部分功能,如:
CSS in JS目前流行的库如下:
ES6标签模板字符串在当做函数调用时的参数的时候,浏览器会按照一种特殊的方式对模板字符串参数进行解析和分隔,如果解析后参数进行打印,那么得到的结果是一个二维数组。
该数组的第一项是分割下来的字符串数组,也是一个数组
该数组的第二项及以后是模板字符串中用${}包裹的变量或者JS表达式
const name = "lilei";
const age = 18;
function test(...args){
console.log(args);
}
test`这是姓名${name},这是年龄${age}`;
/* args数组的打印结果是一个二维数组: */
args = [
0: ["这是姓名",",这是年龄",""],
1: "lilei",
2: 18
]
在安装了styled-components库之后,我们在写样式之前需要做两个准备:
span: ? templateFunction()
div: ? templateFunction()
a: ? templateFunction()
styledComponentId: "sc-AxjAm" // 唯一id
target: "div"
componentStyle: {
baseHash: 400283751
componentId: "sc-AxjAm"
isStatic: false
rules: [
0: "\n\twidth:500px;\n\theight:200px;\n\tbackground-color:pink;\n"
]
staticRulesId: ""
}
安装styled-components库
npm install styled-components@5.1.1 --save
新建同级样式文件styled.js,导入库之后按照模板字符串的语法书写css样式
import styled from "styled-components";
export const HomeWrapper = styled.div`
width:500px;
height:200px;
background-color:pink;
// 结构嵌套
.banner{
font-size:20px;
color:blue;
cursor:pointer;
// 伪元素
&:hover{
color:red;
}
// 伪类元素
&::after{
content:"小尾巴";
}
}
`;
export const H2Wrapper = styled.h2`
font-size:18px;
color:red;
`
一般情况下给组件的根元素来一个Wrapper组件包裹就类似于给根元素一个id值一样,后续的子元素都基于嵌套的写法写在里面就可以了;但是由于这里生成的是class类名,所以如果还是不放心怕其他组件的id选择器进行覆盖的话,可以为某些样式再生成一个组件进行替换,确保样式不会覆盖。
import {
HomeWrapper,
H2Wrapper
}from "./styled.js"
class Home extends PureComponent{
render(){
return(
<HomeWrapper>
<H2Wrapper>这是一个title</H2Wrapper>
<p className="banner">这是一个banner</p>
</HomeWrapper>
)
}
}
主要基于styled-components库中提供的attrs方法以及props属性穿透的特性实现:
styles
attrs方法接收的参数中的对象都可以在写编写样式的时候基于${props=>props.xxx}来进行调用,箭头函数的返回值会作为插值的返回值.
import styled from "styled-components";
export const StyleInput = styled.input.attrs({
placeholder:"请输入您的姓名",
type:"text",
bgColor:"pink"
})`
font-size:20px;
color:blue;
background-color:${props=>props.bgColor}; /* 使用来自attrs中参数*/
border:${props=>props.bd};/* 使用来自组件state中属性 */
`
import {
StyleInput,
} from ‘./styled.js‘
/* Home组件 */
class Home extends PureComponent{
constructor(props) {
super(props);
this.state = {
borderStyle:"1px solid red"
}
}
render(){
return(
<div>
{/* 将state中的属性borderStyle当做参数穿透到样式中*/}
<StyleInput bd={this.state.borderStyle}/>
</div>
)
}
}
实现原理:基于styled(FatherCpn)styles
styled方法接收一个经过styled.tag()styles
增强之后的React组件对象作为参数,返回一个新的React组件对象。新的组件对象会继承其父组件的所有样式,如果有自己的样式可以再进行定义。
StylePrimeryButton组件样式继承了StyleButton组件的样式,对于不同的部分再自己进行定义,这一点和实例属性来覆盖父类的原型属性一个道理。
export const StyleButton = styled.button`
width:100px;
height:40px;
color:#6c6c6c;
background-color:#fff;
border:1px solid #eee;
`
export const StylePrimeryButton = styled(StyleButton)`
color:#24a2ff;
background-color:#23272d;
`
class About extends PureComponent{
render(){
return(
<div>
<StyleButton>普通按钮</StyleButton>
<StylePrimeryButton>主要按钮</StylePrimeryButton>
</div>
)
}
}
从styled-components中导入ThemeProvider这个分享组件,该组件必须传递一个theme属性,属性值就是父组件要共享给每一个子组件的样式,这个样式可以来自于state对象或者props等等。
在编写子组件HomeWrapper的样式的时候,就可以通过${props=>props.theme.xxx}来获取父组件要进行共享的样式,从而达到更高程度的样式复用,减少冗余代码。
export const HomeWrapper = styled.div`
background-color:${props=>props.theme.bgColor};
font-size:${props=>props.theme.lgSize};
`
import {ThemeProvider} from "styled-components";
import {HomeWrapper} from ‘./styled.js‘
class App extends PureComponent{
constructor(){
super();
this.state = {
bgColor:"pink",
lgSize:"40px"
}
}
render(){
return(
<ThemeProvider theme={this.state}>
<Home></Home>
<About></About>
</ThemeProvider>
)
}
}
原文:https://www.cnblogs.com/gaokai/p/15028441.html