Css in JS的思路实现Css样式的组件化(Emotion)

1,610 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,[点击查看活动详情]

前言

前端boy都知道,css是前端开发的必备技能,js搭建起的内容像1,而css则是1后的0,css运用的越好,用户体验感越好,那么项目的价值越高。css作为一门标记语言,主要用于样式定义以及页面布局,但是原生的css具有很多问题:

  1. 全局污染,样式覆盖,命名冲突等常见问题。
  2. 缺乏变量,函数式、模块化等机制,使得开发效率降低,同时遇到一些动态样式变化,通过:class='{}',:style='{}'这种方式并不能很好的满足。
  3. 样式难以复用,无法通过继承、组件等方式对css样式进行复用。

虽然Sass、Less帮助我们增添了变量、混入、继承等概念,但在一定程度上并不能更好的解决以上问题,个人认为特别是组件化,函数式的问题。特别是许多后端同学写后台时对于css这种标记语言非常讨厌,渴望有一套函数式或者组件式写法。

因此Css in JS的思想很久之前就已经提出,并且经过了长期的实践,虽然其存在编译的性能问题以及对于新手使用不友好等问题,但并不妨碍其成为css的新常态,同时也衍生出许多优秀的工具,比如较早的Styled-Components,当我还是前端小白阶段时(现在依然是菜鸡),其使用方式完全颠覆了我对于原生css的概念,今天给大家讲解一款最近使用的@emotion/styled,其使用方式个人认为与Styled-Components相差无几。

emotion官网

一、emotion有哪些优势?

  1. 无需关心className命名问题,其为样式生成了唯一的name,不用担心命名冲突等问题。
  2. 可以搭配TS自定义样式组件,并且根据props动态配置。
  3. 提供了增强 React.createElement 方法jsx,添加了一个 css prop。

二、emotion如何使用?

1.安装

yarn add @emotion/react @emotion/styled

2.基础使用

styled.后一般会跟一个html标签名,例如div、h1、main、article等元素标签,styled.div调用后会返回一个React组件,emotion会自动生成一个HashName加到组件中,并且将``内的样式与组件关联。

import React from 'react'
import styled from '@emotion/styled'
export const AuthenticatedApp=()=>{
    return <Container>
            <Main>
               main
            </Main>
    </Container>
}
// 可以使用自定义变量
const widthNum='20rem'
const Container= styled.div`
    width:${widthNum};
    height: 100vh;
`;
const Main= styled.main`
`;

3.搭配Ts,实现一个自定义组件Row,并能够通过props控制样式

(1)在components下新建lib.tsx

我们希望该组件可以接收三个参数gap,between,marginBottom,分别控制其子元素的marginRight、flex横向布局样式以及marginBottom,css样式名后可以使${(props)=>props.between?'spacebetween':undefined}; 获取组件传来的props,同时根据需求结合props动态返回样式。

import styled from '@emotion/styled';
export const Row=styled.div<{
    gap?:number | boolean,
    between?:boolean,
    marginBottom?:number
}>`
    display:flex ;
    align-items: center;
    justify-content:${(props)=>props.between?'space-between':undefined};
    margin-bottom:${props=>props.marginBottom + 'rem'} ;
    > * {
        margin-top: 0 !important ;
        margin-bottom: 0 !important ;
        margin-right:${props=>typeof props.gap==='number'?props.gap + 'rem':props.gap?'2rem':undefined};
    }
`
(2)组件中使用,Header上可以传入参数gap等。
import React from 'react'
import styled from '@emotion/styled'
import { Row } from 'components/lib';

export const AuthenticatedApp=()=>{
    return <Container>
                <Header gap={true}>
                    <h2>项目</h2>
                    <h2>用户</h2>
                </Header>
            <Main>
                main
            </Main>
    </Container>
}
const Container= styled.div`
    width:20rem;
    height: 100vh;
`;
const Header = styled(Row)``;
const Main= styled.main``;

4.实现样式的继承

styled(样式组件名)的方式可以实现对样式的继承,并在其后做样式的拓展,如下,我们继承了自定义样式组件Row的样式,并且添加了宽度属性。

const Header = styled(Row)`
    width:100rem
`;

基于此,我们在使用组件库如antdesign的组件时,常常会在组件上写内联样式,那么现在不再需要在内联上改变样式,只需如下即可:

import {Button} from 'antd'
const Header = styled(Button)`
    width:100rem
`;

5.提供了增强 React.createElement 方法jsx,添加了一个 css prop。

在标签上写内联样式style是有许多限制的,如不能使用层级选择等,而@emotion/react提供了jsx,进一步拓展了style。

import {jsx} from '@emotion/react'
import React from 'react'

export const AuthenticatedApp=()=>{
    return <div css={{
        backgroundColor:'red',
        '&:hover': { 
            backgroundColor: 'green'
        }
    }}></div>
}

写在最后

css的初级使用其实是非常简单的,但是随着前端技术的发展,不断有更多更复杂的需求接踵而至,Css in JS的思想也是css工程化进程中的一种解决思路,同时其他方向的技术也不断发展,比如Tailwindcss,其类似于bootstrap使用了很多引导程序的语义,定义了一套基本样式,但是它也存在className生成繁乱,不够简洁等问题,所以说不同的技术方案有不同的优缺点,但是我们可以根据实际需求来选择我们的技术方案,比如快速建立一个简单的单页面应用,完全可以使用比如Tailwind等,如果要建立一个复杂的系统,更推荐大家使用Css in JS的相关工具,其动态性、以及组件的可维护性确实非常实用。