最近需要做一个表格组件, 组件需求:
指定行, 列
可以跨行, 跨列
行和行之间有分割线
最终采用 grid 实现需求. 实现的时候遇到一个问题, 如果 CSS 和 JS 分开写, CSS 只能是定值, 没有灵活性. 所以考虑采用 CSS in JS 的形式. 关于 CSS in JS 相关的概念介绍可以参考阮一峰老师的文章: CSS in JS 介绍.
在 GitHub 上找了一下关于这方面的组件, 发现 styled components https://www.styled-components.com/ 非常不错, 简单易上手,
NPM 下载:`npm i styled-components -S`
注意: React <16 需要下载 3.x.x 版本的
根据文档先写一个简单的 demo.
可以看到, 它的实现方式并不是传统的以对象的形式写样式, 而是将需要添加样式的元素加到 styled 对象上, 然后跟一个 (``) 反引号标签, 在里面以正常的 CSS 格式写样式. 然后返回一个组件, 把组件替换原来的 div 即可.
实现效果:
html 代码:
CSS 代码:
刚才我们添加样式的元素是 HTML 元素, 那么给组件添加样式可以么? 实践一下:
const H1 = ({ className }) => <h3 className={className}>我是 App</h3>;
- const HH = styled.H1`
- font-size: 30px;
- color: red;
- `;
运行, 报错:
咋回事? 原来 styled 不支持以 . 符号的形式为组件添加样式, 需要以参数形式传递, 修改上面代码
- const HH = styled(H1)`
- font-size: 30px;
- color: red;
- `;
将 H1 组件以参数形式传递给 styled, 就可以了.
想给元素添加伪元素样式, 子元素样式可以么? 没问题, styled components 支持样式嵌套, 按照类似 Less 或 SCSS 的书写方式就可以了.
- const Container = styled.div`
- width: 300px;
- max-width: 500px;
- min-width: 200px;
- transition: all 1s ease-in-out;
- background-color: rgba(240, 240, 240, 0.9);
- ::after {
- content: 'after';
- display: table;
- color: blue;
- }
- span {
- color: green;
- }
- `;
以上我们写的组件都是在 Class 外面, 那我们要根据 props 设定样式怎么办? styled components 同样支持在 Class 内生成组件, 并接受 props 传递过来的值, 这个 props 并不是我们的 Class 接收的 props, 它是添加样式的元素上的 Props, 意思就是
- class Demo {
- render(){
- return <Styled name="guoshi"></Styled>
- }
- }
- Demo.defaultProps = {
- age: 18
- }
- const Styled = styled.p`
- color: ${props=>{console.log(props)}} // {name: "guoshi",theme:{...}}
- `
如果想使用 Class 的 props 怎么办? 看代码:
- generateStyle = () => {
- const {row, col, justify, data, prefixCls, showborder = true, rowgap = 0, colgap = 0} = this.props;
- const child = [];
- data.map((item,index)=> (item.colSpan || item.rowSpan) ? child.push({index:index+1,colSpan:item.colSpan, rowSpan:item.rowSpan}):null);
- const bordernone = [];
- for(let i = 0; i <row; i++) {
- bordernone.push(1 + i*col);
- }
- const UlContainer = styled.ul.attrs({className: prefixCls})`
- display: grid;
- grid-template-columns: ${()=> {
- let arr = [];
- arr.length = col;
- return arr.fill('1fr').join(' ');
- }};
- grid-template-rows: ${()=> {
- let arr = [];
- arr.length = row;
- return arr.fill('1fr').join(' ');
- }};
- grid-gap: ${rowgap} ${colgap} ;
- justify-items: ${()=> justify || "center"};
- ::before {
- display: none;
- }
- li {
- width: 100% !important;
- }
- ${
- child.map(({index, colSpan, rowSpan}) =>`
- li:nth-child(${index}) {
- grid-column-start: ${index%col === 0 ? index : index%col};
- grid-column-end: ${colSpan ? (colSpan+(index%col === 0 ? index : index%col)) : ((index%col === 0 ? index : index%col)+1)}
- grid-row-start: ${Math.ceil(index/col)};
- grid-row-end: ${rowSpan ? rowSpan+Math.ceil(index/col) : Math.ceil(index/col)+1};
- align-items: start;
- }
- `).join(' ')
- }
- li + li {
- border-left: 1px dashed rgba(0,0,0,0.3);
- }
- ${
- bordernone.map(bordernoneindex=>`
- li:nth-child(${bordernoneindex}) {
- border-left: none;
- }
- `).join(' ')
- }
- `;
- return UlContainer;
- }
提前把最后代码放出来了, styled.ul.attrs({})就是为元素添加额外的属性, 比如 className,placeholder 等, 从代码中可以看到, 无论是 Class 的 props 还是元素自身传进来的 props,styled 都可以接收, 你可以自定义任何你想实现的规则, 更方便我们配置的灵活性.
其实到这里, 使用上没有任何问题了, 关于 keyframes 等规则官网上都有 demo, 也非常容易实现. 想尝试的小伙伴现在就可以码一遍. 不过本章遗留了很多问题:
styled.div & styled(div) 有什么区别
模版字符串中的样式是怎么解析的? 为什么可以嵌套?
为什么会返回一个组件?
来源: https://juejin.im/post/5c25b9e3e51d455e860da92a