谈谈 styled-components
早在刚开始接触 React 的时候就有听闻过 css in js 的理念,中间也简单使用过 styled-components,但用到最后总因为各种原因放弃。这次刚好心血来潮又想重新试试,同时记录一下从个人角度来看 styled-components 的优缺点,以便将来选型时作为参考之用。
基于 create-react-app 搭建的 TypeScript 项目,至于如何加入 styled-components,这里不赘述,按照文档很简单就可以完成了。以下简称为 sc。
使用
简单使用方法如下:
import styled from 'styled-components'
const Title = styled.h1`
font-size: 1.5em;
`;
render(
<Title>
Hello World!
</Title>
);
最终编译出来的是一个独特的 hash 值的样式名,这可以避免我们样式名称被覆盖,而在此之前我们往往需要配置 css modules, 如果是 create react app 创建的项目,往往会用 .module.css 来表示 css 模块。而 sc 则自带了这个特性,我觉得这是他的第一个优点。
styled-components 可以传 props。
import styled from 'styled-components'
const Title = styled.h1<{isRed: boolean}>`
font-size: 1.5em;
color: ${props => props.isRed ? 'red' : 'black'}
`;
const isRed = ...
render(
<Title isRed={isRed}>
Hello World!
</Title>
);
而在使用 styled-components 之前,我们要做到这个效果往往是在 style 里处理,或者是通过样式名来控制
// 在 style 里处理,不方便复用
render(
<h1 style={{ color: isRed ? 'red' : 'black' }}>
Hello World!
</h1>
);
// 通过样式名控制较为繁琐
.red { color: 'red' }
.black { color: 'black' }
render(
<h1 className={isRed ? 'red' : 'black'}>
Hello World!
</h1>
);
这是 styled-components 的第二个优点。
有个场景比较经常出现在维护旧代码的时候,往往有的组件不需要了,我们可以简单移除掉,但是这个组件所使用的样式名却不敢轻易删掉,因为鬼知道这个样式是不是别的地方用到了。而这个则是 sc 中样式组件则不存在这个问题,所以这是第三个优点:便于维护。
以上是我觉得感知比较强烈的优点,当然还有一些别的优点这里就不说了,具体可以查看文档。
说完了优点,说说开发时遇到的缺点吧
第一个就是由于 sc 实际上就是组件,而这个往往会让我分不清哪些是样式组件,哪些是 React 组件,就是显得不够直观。
第二个是有一些组件,实际上只是一行样式就解决了,那么每次都需要重新写一个 sc 组件,反而显得繁琐。官方是推荐可以使用 css prop 来解决
import { css } from 'styled-components/macro'
render(
<h1 css={css`font-size: 1.5em;`}>
Hello World!
</h1>
);
这个功能需要有babel-plugin-styled-components 或者 babel-plugin-macro 的支持,create react app 默认支持了,详情看文档。
如果是 Typescript,由于原先的组件上并没有 css 这个属性,则需要额外在全局引入
import {} from 'styled-components/cssprop'
文档中提到 css prop 实际上也是将组件转为样式组件,而如果是在 typescript 中需要传递自定义 prop,我不知道该如何定义类型?
import { css } from 'styled-components/macro'
const Title = css`
font-size: 1.5em;
color: {props => props.color}
`
render(
// 此时将提示 color 类型错误云云。。
<h1 css={Title} color="red">
Hello World!
</h1>
);
第三个是样式复用,即如果我有相同的样式想要用在不同的组件上,sc 也是推荐使用 css props,而 css props 的体验是在是不敢说很好。
就我个人而言目前主要是这三点用着不爽。
原理
那么 sc 为什么可以通过 styled来给组件添加样式的呢?
es6 提供了 `` 作为模板字符串,我们通常的用法
const name = `${firstName}${secondName}`
而如果 ``是跟随在一个函数时,则该函数将被调用来处理这个模板字符串,这个称为标签模板
console.log`hi`
// 等同于
console.log('hi')
关于这部分可以查阅 MDN 相关文档
styled.div`
color: red
`
// 等同于
styled.div(['\n color: red;\n'])
在编译后的代码中可以看到一段如下代码,不难猜测出 Object(i.a) 就是 styled.div
var n = Object(i.a)(['\n color: red;\n'])
styled.div等同于 styled('div'),因而 styled 可以看作一个高阶组件
const StyledComponent = styled(Component)(styles)
后续
为了解决一两行代码也要用 css prop 的情况,我引入了 tailwindcss,这个 css 框架中的原子性类名理念非常契合我的问题。我觉得这两个框架搭配写起来还是比较顺畅的。