做爲一個前端,毫無疑問css確定是最基礎的一項技能之一。css是一個標記語言,沒有編程語言的諸多特性,好比變量定義,複用,嵌套等,因此相應的開發效率也受到限制。
在追求效率和自動化的當下,涌現了一批解決方案,像是css預編譯語言Less, Sass等,解決css命名衝突的css-modules,react中css的最佳實踐styled-components等。本篇文章不在於探討css的技巧學習,而在於討論css的這些提高開發效率的方案。javascript
Less, Sass, Stylus等 css預編譯語言
,給css賦予了編程特性。拿 Less
來講,它擴展了 CSS 語言,增長了變量、Extend、Mixin、函數等特性,也支持import導入文件,使 CSS 更易維護和擴展。本篇簡單介紹一下Less的一些特性,詳細的教程能夠上Less官網查看。css
咱們能夠在 命令行
直接使用less,也能夠經過 node api
去使用less,或者經過 webpack
,gulp
,grunt
等的 less插件
去使用,甚至能夠在瀏覽器端使用,很是靈活。這裏簡單說一下在命令行中使用less。html
$ npm i less -g # 當less被安裝以後,就可使用全局命令lessc $ lessc bootstrap.less bootstrap.css
變量使css代碼更易維護。
前端
好比有個主色 #ef8376
,在整個樣式表中,咱們有多處使用這個顏色。若是主色變更的話,好比主色要變成 #000
,咱們就要手動去全局替換這個變量,而有一些 #ef8376
咱們卻不但願替換掉,這樣就形成了極大的困擾。vue
若是咱們使用less的話,就能夠這麼寫:java
@primaryColor: #ef8376; .banner { background-color: @primaryColor; .text { color: @primaryColor; border-color: #ef8376; } }
咱們要修改主色,只須要將 @primaryColor
修改成 '#000'便可。node
Extend讓咱們能夠用僞類的寫法去合併一些類
。react
好比:webpack
nav ul { &:extend(.inline); background: blue; } .inline { color: red; }
會編譯成:git
nav ul { background: blue; } .inline, nav ul { color: red; }
Mixin既有Extend繼承已有類的特性,也有其餘高級的特性,好比支持變量,支持像使用方法同樣使用mixin
支持變量
.foo (@bg, @color: '#000') { background: @bg; color: @color; } .unimportant { .foo(#f5f5f5); } .important { .foo(#121212) !important; }
會編譯成:
.unimportant { background: #f5f5f5; color: #000; } .important { background: #121212 !important; color: #000 !important; }
像方法同樣使用Mixin
.count(@x, @y) { @margin: ((@x + @y) / 2); @padding: ((@x + @y) / 4) } div { margin: .count(16px, 16px)[@margin]; padding: .count(16px, 16px)[@padding]; } .loop(@counter) when (@counter > 0) { .loop((@counter - 1)); // next iteration width: (10px * @counter); // code for each iteration } .text { .loop(5); // launch the loop }
會編譯成:
div { margin: 16px; padding: 8px; } .text { width: 10px; width: 20px; width: 30px; width: 40px; width: 50px; }
// head.less .banner { background-color: red; }
// footer.css .footer { background-color: green; }
@import './head.less'; @import css './footer.css';
會編譯成:
.banner { background-color: red; } .footer { background-color: green; }
Less支持一些經常使用的輔助方法
好比darken
和lighten
用來加深或淡化顏色。
body { background-color: darken(hsl(90, 80%, 50%), 20%); color: lighten(hsl(90, 80%, 50%), 20%); }
會編譯成:
body { background-color: #4d8a0f; color: #b3f075; }
css-modules 相較於 Less 來講有所不一樣,css-modules 只是拓展了 css 的寫法,解決了css的塊做用域和全局做用域,而不是將css變成一門編程語言。
Css一直以來都有一個問題,就是css定義的類都是全局的,咱們雖然能夠經過不一樣的命名空間或是加前綴的方式去避免類的混淆和衝突,可是在寫法上卻不是那麼的乾淨利落,並且一旦重構css的話,也會形成很大的困擾。
爲了讓咱們能隨意的寫類名而不須要考慮衝突或是覆蓋,css-modules 便出現了。
css-modules提供了 塊做用域 :local
和 全局做用域 :global
,這兩個特性就能很好的避免css的命名衝突。
首先來講一下怎麼使用 css-modules。
當咱們在使用webpack的時候,最簡單的用法是經過 css-loader 來開啓對 css-modules 的支持。以下:
{ test: /\.css$/, use: [ { loader: 'css-loader', options: { modules: true, // 開啓對css-modules的支持 localIdentName: '[name]__[local]___[hash:base64:5]' // 生成的類名的格式 } } ] }
同時能夠配合less-loader 和 postcss使用。注意:在結合less-loader的時候可能出現對url的兼容問題。見:https://github.com/webpack-co... 。並且 less-loader 的維護者認爲結合 less-loader 和 css-modules沒什麼必要。。
css-modules提供了兩個關鍵字,:local
和 :global
。
好比這種寫法:
// App.css :local(.banner) { background: red; } :local(.banner .text) { color: yellow; } .center { color: green; } :global(.global-class-name) { color: blue; }
會編譯成:
.App__banner___3NbRo { background: red; } .App__banner___3NbRo .App__text___2j1Ht { color: yellow; } .App__center___3eDJo { background: green; } .global-class-name { color: blue; }
:global
聲明的類不會被編譯,會保持不變。
同時,咱們在js中引入css,寫法以下:
/** * styles是什麼呢?styles實際上是一個通過處理過的類名的集合。 * * 好比上邊這個css文件,處理後的style對象是這樣的: * * { * banner: 'App__banner___3NbRo', * text: 'App__banner___3NbRo App__text___2j1Ht', * center: 'App__center___3eDJo' * } * * 這樣咱們就能夠理解爲何css-modules能夠避免明明衝突了。 * 命名都按照咱們設置的hash規則重寫了,保證了類名的惟一,而且在生成的html結構裏也進行了替換,還何來衝突? */ import styles from './App.css'; import React from 'react'; const html = () => { return <div class={styles.banner}> <span class={style.text}>HAHAHAHHAHAHA</span> </div>; }; export default html;
css-modules支持多個類的混合組成。好比:
.colorRed { color: red } .text { composes: colorRed; background: #000; }
會編譯成:
.App__colorRed___yoG_f { color: red } .App__text___2j1Ht { background: #000; }
能夠看到,生成的css中並無任何的變化,那這個composes作了什麼呢?其實在經過js引用的對象內發生了變化。以下:
{ "colorRed": "App__colorRed___yoG_f", "text": "App__text___2j1Ht App__colorRed___yoG_f" }
那麼在經過 styles.text
使用 text
類的時候,其實也同時使用了 colorRed
類,達到了混合組成的效果。
css-modules
支持引用其餘文件的類。
好比:
// green.css .green { color: green; }
// text.css .text { background-color: red; composes: green from './green.css'; }
會編譯成:
.green__green___1v20L { color: green; } .text__text__2jfs0 { background-color: red; }
其實跟 二
同樣,生成的css並無什麼改動,其實改變的是生成js對象的內容:
import styles from './text.css'; // styles = {green: 'green__green___1v20L', text: 'text__text__2jfs0 green__green___1v20L'}
styled-components, 多是React中css的最佳實踐了,若是你喜歡,你也能夠叫它styled-react-components
: )。想象一下,像寫react組件同樣去寫css,是一種什麼樣的體驗?
以下,你能夠這樣來寫樣式:
import React from 'react'; import styled from 'styled-components'; const Title = styled.h1` font-size: 1.5em; text-align: center; color: palevioletred; `; const Wrapper = styled.section` padding: 4em; background: papayawhip; `; export default () => <Wrapper> <Title>Hello World, this is my first styled component!</Title> </Wrapper>;
styled-components
會自動幫你在 運行時
生成一個樣式表,插入到 <head>
下的 <style>
標籤中,好比上邊的代碼,會在運行是生成以下代碼:
<head> <style data-styled-components> /* sc-component-id: model__Title-cooNNd */ .model__Title-cooNNd {} .jHitSF{font-size:1.5em;text-align:center;color:palevioletred;} /* sc-component-id: model__Wrapper-bEJrHK */ .model__Wrapper-bEJrHK {} .ipFfju{padding:4em;background:papayawhip;} </style> </head> <body> <section class="model__Wrapper-bEJrHK ipFfju"> <h1 class="model__Title-cooNNd jHitSF">Hello World, this is my first styled component!</h1> </section> </body>
咱們能夠看到,咱們在js中寫的樣式,被插入到了 <style>
中,而且生成了一個隨機的類名,並且這個類名,也是被 react-dom
生成的DOM結構所引用。
受益於 styled-components
,咱們貫徹了 react
的 萬物皆組件
的思想,使咱們在css的組件化上又推動了一步(發佈一個純css組件試試?) : )
在這篇文章裏,我會簡單探討一下 style-components
的用法和特性。
styled-components
通常配合着 react
使用,固然也支持 vue
(vue-styled-components)。拋開這兩個來講,你也能夠直接在原生js下使用:
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
咱們這裏講配合 react
的用法。
1、首先,安裝依賴
$ npm i styled-components # 配合着babel來使用 $ npm i -D babel-plugin-styled-components
2、配置 .babelrc
(固然,咱們須要安裝 webpack
,配置webpack的config,而且須要須要安裝 babel-preset-env
和 babel-preset-react
,這裏不贅述)
{ "presets": ["env", "react"], "plugins": ["styled-components"] }
通過以上簡單的配置以後,就能夠在項目中使用 styled-components
了。
固然,如今的 styled-components
也是支持了 stylelint 和 jest,因此,你也不用擔憂樣式檢查和測試了 :)
下邊兒說一下 styled-components
的一些用法和特性。 官方文檔在這兒: https://www.styled-components...
你能夠傳props給組件,讓組件根據所傳的props的值動態改變樣式。
const Button = styled.button` /* 根據props的值動態改變樣式的值 */ background: ${props => props.primary ? 'palevioletred' : 'white'}; color: ${props => props.primary ? 'white' : 'palevioletred'}; `; render( <div> <Button>Normal</Button> <Button primary>Primary</Button> </div> );
const Button = styled.button` color: palevioletred; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; `; // 建立一個新Button組件,繼承自Button,並對Button進行樣式添加和覆蓋 const TomatoButton = styled(Button)` color: tomato; border-color: tomato; `; render( <div> <Button>Normal Button</Button> <TomatoButton>Tomato Button</TomatoButton> </div> );
好比,你建立了一個Button組件,你想把button
標籤變成a
標籤,可是樣式仍是button
的樣式。那麼你能夠經過 withComponent
方法輕鬆作到。
const Button = styled.button` display: inline-block; color: palevioletred; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; `; // 把<button>標籤替換成<a>標籤 const Link = Button.withComponent('a') // 繼承Link組件 const TomatoLink = styled(Link)` color: tomato; border-color: tomato; `; render( <div> <Button>Normal Button</Button> <Link>Normal Link</Link> <TomatoLink>Tomato Link</TomatoLink> </div> );
// 這個keyframe會隨機生成一個name const rotate360 = keyframes` from { transform: rotate(0deg); } to { transform: rotate(360deg); } `; const Rotate = styled.div` display: inline-block; animation: ${rotate360} 2s linear infinite; padding: 2rem 1rem; font-size: 1.2rem; `; render( <Rotate>< 💅 ></Rotate> );
const Content = styled.div` background: papayawhip; height: 3em; width: 3em; @media (max-width: 700px) { background: palevioletred; } `; render( <Content /> );
styled-components
支持嵌套寫法,這個特性是從 Sass
移植過來的。
const EqualDivider = styled.div` display: flex; margin: 0.5rem; padding: 1rem; background: papayawhip; ${props => props.vertical && 'flex-direction: column;'} > * { flex: 1; &:not(:first-child) { ${props => props.vertical ? 'margin-top' : 'margin-left'}: 1rem; } } `; const Child = styled.div` padding: 0.25rem 0.5rem; background: palevioletred; `; render( <div> <EqualDivider> <Child>First</Child> <Child>Second</Child> <Child>Third</Child> </EqualDivider> <EqualDivider vertical> <Child>First</Child> <Child>Second</Child> <Child>Third</Child> </EqualDivider> </div> );
好比你在項目中引入了 bootstrap.css
,應該怎麼和bootstrap
中的類配合使用呢?
const Button = styled.button.attrs({ // 生成的classList中會包含small className: 'small' })` background: black; `; render( <div> <Button>Styled Components</Button> <Button>The new way to style components!</Button> </div> );
怎麼樣覆蓋高優先級的樣式呢?固然咱們能夠經過 !important
來作,不過 styled-components
更推薦下邊這種作法:
const MyStyledComponent = styled(AlreadyStyledComponent)` &&& { color: palevioletred; font-weight: bold; } `;
每一個 &
替換爲生成的類,那麼生成的CSS是這樣的:
.MyStyledComponent-asdf123.MyStyledComponent-asdf123.MyStyledComponent-asdf123 { color: palevioletred; font-weight: bold; }
那麼怎麼覆蓋內聯樣式呢?以下:
const MyStyledComponent = styled(InlineStyledComponent)` &[style] { font-size: 12px !important; color: blue !important; } `;
styled-components
顛覆了傳統的樣式寫法,像寫組件同樣寫css,配合 react
恰到好處 :)
至於在 Less、css-modules 和 styled-components 中到底選擇哪個,就要看你的應用場景和需求了。
本章完
個人Github: https://github.com/PengJiyuan