Css in Js 一次實踐

navigatorOpera發表於2018-12-28

最近需要做一個表格元件,元件需求:

  1. 指定行、列
  2. 可以跨行、跨列
  3. 行和行之間有分割線

最終採用grid實現需求。實現的時候遇到一個問題,如果css和js分開寫,css只能是定值,沒有靈活性。所以考慮採用css in js的形式。關於css in js相關的概念介紹可以參考阮一峰老師的文章:css in js 介紹

在github上找了一下關於這方面的元件,發現styled components 非常不錯,簡單易上手,

npm下載:`npm i styled-components -S`
複製程式碼

注意: React < 16 需要下載3.x.x版本的

根據文件先寫一個簡單的demo。

程式碼

可以看到,它的實現方式並不是傳統的以物件的形式寫樣式,而是將需要新增樣式的元素加到styled物件上,然後跟一個(``)反引號標籤,在裡面以正常的css格式寫樣式。然後返回一個元件,把元件替換原來的div即可。

實現效果:

2.png

html程式碼:

3.png

css程式碼:

4.png

剛才我們新增樣式的元素是html元素,那麼給元件新增樣式可以麼?實踐一下:

const H1 = ({ className }) => <h3 className={className}>我是App</h3>;
const HH = styled.H1`
  font-size: 30px;
  color: red;
`;
複製程式碼

執行,報錯:

Css in Js 一次實踐

咋回事?原來styled不支援以 . 符號的形式為元件新增樣式,需要以引數形式傳遞,修改上面程式碼

const HH = styled(H1)`
  font-size: 30px;
  color: red;
`;
複製程式碼

H1元件以引數形式傳遞給styled,就可以了。

Css in Js 一次實踐

想給元素新增偽元素樣式,子元素樣式可以麼?沒問題,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,也非常容易實現。想嘗試的小夥伴現在就可以碼一遍。不過本章遺留了很多問題:

  1. styled.div & styled(div) 有什麼區別
  2. 模版字串中的樣式是怎麼解析的?為什麼可以巢狀?
  3. 為什麼會返回一個元件?

下一章,我會根據styled componnets原始碼解答上述問題。

最後,祝生活愉快。

相關文章