0


CSS-In-JS

CSS-In-JS

git地址

什么是CSS-IN-JS

实际上这就是一种解决方案,在这种解决方案中,它提倡我们使用JavaScript代码去编写CSS代码,也就是说它提倡我们集成 CSS 代码在 JavaScript ⽂件中.

一.为什么会有CSS-IN-JS

CSS-IN-JS 是 WEB 项⽬中将 CSS 代码捆绑在 JavaScript 代码中的解决⽅案.

1.1 缺乏作用域

实际上在现有的react项目中,我们已经把html代码和JavaScript代码写在一起了,如果在采用CSS-In-Js解决方案的话,我们就是把html代码、css代码以及JavaScript代码写在一起了.这与我们当前的认知是背道而驰的,在我们当前的认知当中,应该把css代码,JavaScript代码,html代码进行分离编写.这里面的原因是什么呢?实际上是开发方式的变化.以前我们开发前端项目的时候都是以页面为单位的,比如说我们要开发一个首页,一个列表页或者开发一个详情页,这时候的css文件都是通过link标签引入到html文件中的,由于css本身没有作用域的概念,所以css文件中的代码都会作用到html文件中.这在当时是没有问题的,但是现在我们在开发项目的时候都是以组件为单位的,既然是以组件为单位的,我们就希望css代码就只应用于某一个组件,也就是css代码只在某个组件内生效.组件与组件中的css代码不会产生冲突.如果要实现这样的效果,就需要css有作用域的概念,但是css本身没有这个概念,如果采用CSS-IN-JS方案的话,我们就可以通过JavaScript的作用域去模拟css作用域,这样的话css代码就可以只应用于组件内部了.

1.2 增加组件的独立性和可移植性.

如果说我们把css代码都写在不同的css文件中,当我们想把这个组件想放到其他的地方的时候,在移动的过程中,我们可能会少复制某一个css文件,如果采用CSS-IN-JS方案的话,我们把所有的CSS代码都写在了JavaScript当中,这样的话这个组件就是一个单独的文件,当我们想去移动这个组件的时候,我们只要去移动这个文件本身就行了,而不用去当心它是否还会有其它的依赖文件.

1.3 缺乏动态功能

CSS本身缺乏动态功能,不能根据条件去给某一个元素添加样式如果写在JavaScript中,就可以用JavaScript的动态功能去添加样式了.

总结:旨在解决 CSS本身的局限性, 例如缺乏动态功能, 作⽤域和可移植性.

二.CSS-IN-JS 介绍

2.1 优点

  1. 让 CSS 代码拥有独⽴的作⽤域, 阻⽌ CSS 代码泄露到组件外部, 防⽌样式冲突.
  2. 让组件更具可移植性, 实现开箱即⽤, 轻松创建松耦合的应⽤程序
  3. 让组件更具可重⽤性, 只需编写⼀次即可, 可以在任何地⽅运⾏. 不仅可以在同⼀应⽤程序中重⽤组件, ⽽且可以在使 ⽤相同框架构建的其他应⽤程序中重⽤组件.
  4. 让样式具有动态功能, 可以将复杂的逻辑应⽤于样式规则, 如果要创建需要动态功能的复杂UI, 它是理想的解决⽅案.

2.2 缺点

  1. 为项⽬增加了额外的复杂性.
  2. ⾃动⽣成的选择器⼤⼤降低了代码的可读性

三.Emotion 库

3.1 Emotion 介绍

Emotion 是⼀个旨在使⽤ JavaScript 编写 CSS 样式的库.

npm install @emotion/core @emotion/styled

使用:

import React from "react";
export default function EmoDefault() {
    //添加一个css属性,接收一个对象作为参数,在这个对象中我们就可以写css样式了
    return (<div css={{ width: '200px', height: '50px', background: 'red' }}>EmoDefault</div>)
}

写完后我们发现样式并没有生效,这是因为在默认情况下,我们的项目并不知道如何去解析css属性,所以我们还需要做一下babel配置

3.2 css属性支持

css属性支持有两种配置方式:

  • 1 JSX Pragma

通知 babel, 不再需要将 jsx 语法转换为 React.createElement ⽅法, ⽽是需要转换为 jsx ⽅法
InputOutputBefore

<img src='1.png' />
React.createElement('img',{src:'1.png'})

After

<img src='1.png' />
jsx('img',{src:'1.png'})
/** @jsx/jsx */
import {jsx} from '@emotion/core'
  • 2.Babel Preset(推荐)
  1. npm run eject
  2. npm i @emotion/babel-preset-css-prop
  3. 在 package.json ⽂件中找到 babel 属性, 加⼊如下内容
"babel": {
    "presets": [
      "react-app",
      "@emotion/babel-preset-css-prop"
    ]
  }

3.3 css ⽅法

css方法的作用也是为元素去添加样式,css方法和css属性是配合在一起使用的,通过css方法,我们可以把原本写在行内的样式拿到外部去写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R32YyEcY-1675837050815)(./imgs/img2.png)]

调用css方法会返回一个这样的对象,name就是会给标签加上的类,styles就是加上的样式

css方法有两种调用方式:

import { css } from "@emotion/core";
import React from "react";
//第一种:String Styles(以模板字符串的方式去写样式)(推荐)
const style=css`
    width:200px;
    height:50px;
    background:red
`
//第二种:Object Styles(以对象的方式去写样式)
const style2=css({
    width:200,
    height:50,
    background:'red'
})

export default function CssFunc(){
    return (
        <>
            <div css={style}>css方法调用-模板字符串</div>
            <div css={style2}>css方法调用-对象</div>
        </>
    )
}

3.4 属性优先级

props 对象中的 css 属性优先级⾼于组件内部的 css 属性.

在调⽤组件时可以在覆盖组件默认样式.

import { css } from "@emotion/core";
import React from "react";
const style=css`
    width:200px;
    height:50px;
    background:red
`
function Child(props){
    return (
        <>
            <div css={style}>css优先级</div> 
        </>
    )
}
export default function CssPriority(){
    return (
        <Child />
    )
}

我们看到的效果是这样的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1XV0pMEc-1675837050816)(./imgs/img3.png)]

然后我们做些修改

import { css } from "@emotion/core";
import React from "react";
const style=css`
    width:200px;
    height:50px;
    background:red
`
const style2=css`
    background:orange
`
function Child(props){
    return ( 
            //会把App.js中传递过来的css对原有的css进行覆盖
            <div css={style} {...props}>css优先级</div>  
    )
}
export default function CssPriority(){
     //这个css其实就是子组件里的props.css
    return (
        <Child css={style2} />
    )
}

然后我们再看效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TNoKPnnv-1675837050816)(./imgs/img4.png)]

所以props 对象中的 css 属性优先级⾼于组件内部的 css 属性.

3.5 Styled Components 样式化组件

样式化组件就是⽤来构建⽤户界⾯的,是 emotion 库提供的另⼀种为元素添加样式的⽅式,它可以把普通的标签升级为一个带自定义样式的组件.

styled是一个对象,在这个对象下面有很多的方法,对应的是标签名称,调用这个方法会把对应的标签转化为样式化组件.

3.5.1 创建样式化组件

样式化组件有两种创建方式:

import styled from "@emotion/styled";
import React from "react";
//第一种:String Styles(模板字符串方式)
const Button = styled.button`
  width:130px;
  height:40px;
  background:red;
  color:black
`
//第二种: Object Styles(对象方式)
const Container = styled.div({
  width: '100%',
  height: 100,
  background: 'red',
  color:'yellow'
})

export default function CssComponent() {
  return (
      <>
          <Container>
              样式化组件-对象写法
              <Button> 样式化组件-字符串写法</Button>
          </Container>
      </>
  )
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PiXf8ZpB-1675837050817)(./imgs/img5.png)]

3.5.2 根据 props 属性覆盖样式

  import styled from "@emotion/styled";
  import React from "react";
   //第一种:String Styles(模板字符串方式)
  const Button = styled.button`
      width:130px;
      height:40px;
      background:red;
      color:${props => props.ftColor || 'black'}
  `
  //第二种:Object Styles(对象方式)
  // const Container = styled.div(props=>({
  //     width: '100%',
  //     height: 100,
  //     background:props.bgColor||'red',
  //     color: 'yellow'
  // }))
  //第三种写法:Object Styles(对象方式),第一个参数是一个对象,就是默认的样式配置,第二个参数是一个箭头函数,返回一个对象,有这个属性的时候会对上面的进行覆盖
  const Container = styled.div({
      width: '100%',
      height: 100,
      color: 'yellow'
  }, props => ({
      background: props.bgColor || 'red',
  }))
  
  export default function CssComponent() {
      return (
          <>
              <Container bgColor='blue'>
                  样式化组件根据 props 属性覆盖样式-对象写法
                  <Button ftColor='blue'> 样式化组件根据 props 属性覆盖样式-字符串写法</Button>
              </Container>
          </>
      )
  }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YmWWuZx2-1675837050817)(./imgs/img6.png)]

3.5.3 为任何组件添加样式

包括了普通的react组件

1.创建一个组件

function ReactCom() {
    return (
        <div>
            为react组件添加样式
        </div>
    )
}

2.使用styled方法返回一个新组件

//把组件当做参数传入styled方法里,模板字符串里写的样式就是为组件添加的样式
const CssReactCompStr=styled(ReactCom)`
    heihgt:100px;
    color:red
`
//对象写法
const CssReactCompObj=styled(ReactCom)({
    heihgt:100,
    color:'orange'
})

3.添加className

//调用了styled方法之后,普通组件的props里会有className,我们把它赋给里面的标签
function ReactCom({className}) {
    return (
        <div className={className}>
            为react组件添加样式
        </div>
    )
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EcpandYS-1675837050817)(./imgs/img7.png)]

3.5.4 通过⽗组件设置⼦组件样式

需求:当子组件单独调用的时候和子组件被父组件包裹的时候显示的样式要不一样

import React from "react";
import styled from "@emotion/styled";
const Child = styled.div`
    color:red
`
//通过${}{}去定义特定的子组件,比方说${Child}{color:green},当这个子组件被Father包裹的时候应用里面的样式
//模板字符串写法
const Father = styled.div`
    ${Child}{
        color:green
    }
`
//对象写法
// const Father = styled.div({
//     [Child]: {
//         color: 'green'
//     }
// })
export default function CssFC() {
    return (
        <>
            <Child>子组件单独使用</Child>
            <Father><Child>子组件被父组件包裹</Child></Father>
        </>
    )
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4idI07Jy-1675837050818)(./imgs/img8.png)]

3.5.5 嵌套选择器 &

& 表示元素本身

import React from "react";
import styled from "@emotion/styled";
//&>span 表示这个Container的子级span
const Container = styled.div`
    color:red;
    &>span{
        color:pink
    }
` 
export default function CssNesting() {
    return (
        <Container>
            外层样式
            <span>span样式</span>
        </Container>
    )
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Hm81AdH-1675837050818)(./imgs/img9.png)]

3.5.6 as属性

要使⽤组件中的样式, 但要更改呈现的元素, 可以使⽤ as 属性

例子:比方说我定义了一个样式化组件Button,我想使用里面的样式,但是我不想用button这个标签,想换成a标签

import React from "react";
import styled from "@emotion/styled";
const Button = styled.button`
    color:red;
`
export default function CssAs() {
    return (
        <Button as='a' href='https://www.baidu.com/'>as属性</Button>
    )
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cSj0Ps08-1675837050818)(./imgs/img10.png)]

3.6 样式组合

在样式组合中, 后调⽤的样式优先级⾼于先调⽤的样式.

如下:

import React from "react";
import { css } from "@emotion/core";
const style=css`
    color:red;
    background:yellow;
`
const style2=css`
    color:black;
`
export default function CssComb() {
    return (
        //style2中的样式优先级会高于style
        <div css={[style,style2]}>css样式组合</div>
    )
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-avTIFp05-1675837050818)(./imgs/img11.png)]

3.7 全局样式

定义全局样式需要借助于Global组件

CssGlobal.js

import React from "react";
export default function CssGlobal() {
    return (
        <a href='https://www.baidu.com/'>测试全局样式的a标记</a>
    )
}

App.js

import { css,Global } from '@emotion/core';
import CssGlobal from './pages/CssGlobal';
const globalStyles=css`
  body{
    margin:0;
  }
  a{
    text-decoration:none;
    background:yellow;
  }
`
function App() {
  return (
    <div >
      <Global styles={globalStyles}/>
      <CssGlobal />
    </div>
  )
}

export default App;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AFJeRoOJ-1675837050819)(./imgs/img12.png)]

3.8 关键帧动画

import React from "react";
import { css,keyframes } from "@emotion/core";
//通过keyframes方法创建个动画,跟css动画一样的写法
const ani=keyframes`
    0%{
        width:20px;
        background:red;
    }
    100%{
        width:200px;
        background:yellow;
    }

`
const style=css`
    height:50px;
    color:red;
    animation:${ani} 5s ease infinite;
`
export default function CSSAnimation() {
    return (
        <div css={style}>关键帧动画</div>
    )
}

3.9 主题

下载主题模块

npm i emotion-theming

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
//    1.引⼊ ThemeProvider 组件,这个组件的作用就是存储主题样式的,方便其它组件去获取主题样式
import { ThemeProvider } from 'emotion-theming';
//    2.添加主题内容
const theme = {
  colors: {
    primary: 'tomato'
  }
};

ReactDOM.render(
    //3.将 ThemeProvider 放置在视图在最外层,将主题内容赋值给theme属性
  <ThemeProvider theme={theme}><App /></ThemeProvider>,
  document.getElementById('root')
);

组件获取主题样式

import React from "react";
import { css } from "@emotion/core";
import { useTheme } from "emotion-theming";
//第一种方式获取
const themeColor = props => css`
    color:${props.colors.primary}
`

export default function CssTheme() {
    //第二种方式获取,通过调用钩子函数返回我们定义的那个主题对象
    const theme = useTheme()
    console.log(theme)
    const themeFont=css
    return (
        <>
            <div css={themeColor}>向css属性传递一个函数</div>
            <div css={{fontSize: theme.font}}>使用钩子函数</div>
        </>
        
    )
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aoIdLCMz-1675837050819)(./imgs/img13.png)]

标签: javascript css 前端

本文转载自: https://blog.csdn.net/weixin_45549737/article/details/128934974
版权归原作者 Ne£ 所有, 如有侵权,请联系我们删除。

“CSS-In-JS”的评论:

还没有评论