【CSS】來(lái)自新時(shí)代的CSS

?作者:陳大魚頭?github:?KRISACHAN[1]
前言
最近聽說(shuō)TypeScript3.7添加了對(duì)Optional Chaining[2]的支持,然后就想著給魚頭的腳手架ying-template[3]的TS版本升級(jí),然后在命令行發(fā)現(xiàn)這樣的一句信息:
'postcss-cssnext' 已經(jīng)被 'postcss-preset-env'代替了。詳情請(qǐng)查看 https://moox.io/blog/deprecating-cssnext/
其實(shí)魚頭的腳手架里早就把postcss-cssnext換成了postcss-preset-env,不過(guò)一直沒(méi)刪,但是看到這句話之后,處于好奇,就去翻了翻PostCSS的官網(wǎng),然后又思考了下這些年CSS的發(fā)展歷程,遂有這篇文章的出爐。
淺談現(xiàn)代化的CSS
從1997年?CSS1.0?發(fā)布到如今,從最開始只支持簡(jiǎn)單的文字排版到如今已經(jīng)可以做出酷炫的3D動(dòng)畫,CSS已經(jīng)走過(guò)了22個(gè)年頭,其發(fā)展如圖所示:

[圖片來(lái)自MDN[4]]
隨著互聯(lián)網(wǎng)的發(fā)展,人們對(duì)網(wǎng)頁(yè)的要求已經(jīng)是從只要展示圖文就好變成了各種交互跟視覺(jué)效果都需要有著更多的體驗(yàn)要求。CSS為此也是不斷的更新著。
隨著web業(yè)務(wù)日益復(fù)雜化和多元化,前端開發(fā)也從單純的web page轉(zhuǎn)變成web app,在此也誕生了“前端工程化”的概念,一個(gè)完備的web app往往會(huì)很大很復(fù)雜,甚至?xí)泻芏嗳斯餐S護(hù),以往的拼頁(yè)面,寫jQuery已經(jīng)是不足以支撐現(xiàn)代的需求。同樣的,CSS也是如此,不再是內(nèi)聯(lián)寫幾個(gè)margin,padding或者HTML一股腦引入幾個(gè)CSS就足夠的,而且由于人員配置的增多,不同的開發(fā),命名習(xí)慣,樣式是否會(huì)沖突也是必須要考慮的。
除了工程問(wèn)題,還有就是CSS與瀏覽器之間的關(guān)系也是我們不得不考慮的,雖然CSS發(fā)展的很快,但是瀏覽器對(duì)CSS新特性支持的進(jìn)度確實(shí)非常緩慢的。所以雖然某些屬性已經(jīng)推出了很多年,但是也往往因?yàn)闉g覽器的原因而無(wú)法進(jìn)行大規(guī)模的使用。
雖然在實(shí)際開發(fā)過(guò)程中,CSS有著這樣那樣讓人無(wú)法忽略的問(wèn)題,但是“方法總比困難多”,在前端界也有許多熱心的大牛們?cè)趪L試著解決這些問(wèn)題。這次讓魚頭與大家一起分享下這些與CSS相關(guān)的技巧與方法。
最初的CSS模塊化 —— CSS命名規(guī)則
命名一直是開發(fā)者比較頭疼的問(wèn)題,在前端里,除了JS各種變量的命名,還有元素class的命名,雖然我們可以隨意起名,愿意的話甚至可以使用
.a .b .c等無(wú)意義的規(guī)則來(lái)命名,但是如果是一個(gè)長(zhǎng)期的,大型的或多人協(xié)作的項(xiàng)目里這么命名,恐怕容易被人胖揍。這次我們來(lái)分享下業(yè)界常用的用來(lái)防挨揍的命名規(guī)則。
OOCSS(Object-Oriented CSS)
OOCSS有兩個(gè)編寫原則:
?結(jié)構(gòu)與樣式分離?容器與內(nèi)容分離
我們來(lái)看看官網(wǎng)的一個(gè)例子:

grab
Body
在這里.mod是父類,所有的類都是繼承自它,.grab便是子類。
至于.top、.inner與bottom,顧名思義就是不同位置的子盒子。
這里是以“容器”為命名法則。
BEM
BEM 是塊(Block)、 元素(Element)、修飾符( Modifier)的單詞集合。
在選擇器中,我們用以下三種符號(hào)來(lái)表示以上內(nèi)容
?-?中劃線 :僅作為連字符使用,表示某個(gè)塊或者某個(gè)子元素的多單詞之間的連接記號(hào)。?__?雙下劃線:雙下劃線用來(lái)連接塊和塊的子元素?_?單下劃線:?jiǎn)蜗聞澗€用來(lái)描述一個(gè)塊或者塊的子元素的一種狀態(tài)
就像這樣:type-block__element_modifier
官網(wǎng)的例子如下:

.button {display: inline-block;border-radius: 3px;padding: 7px 12px;border: 1px solid #D5D5D5;background-image: linear-gradient(#EEE, #DDD);font: 700 13px/18px Helvetica, arial;}.button--state-success {color: #FFF;background: #569E3D linear-gradient(#79D858, #569E3D) repeat-x;border-color: #4A993E;}.button--state-danger {color: #900;}Normal buttonSuccess buttonDanger button
SMACSS
SMACSS,一個(gè)長(zhǎng)得很像OOCSS的規(guī)則。
核心只有以下6個(gè):
?Base:頁(yè)面的基本樣式命名規(guī)則?Layout:布局命名規(guī)則?Module:模塊規(guī)命名規(guī)則?State:狀態(tài)命名規(guī)則?Theme:主題命名規(guī)則?Changing State:可變狀態(tài)的命名規(guī)則
修飾符是--,子模塊是__
官網(wǎng)的例子如下:

#header { … }#primarynav { … }#maincontent { … }
為CSS賦能 —— 預(yù)處理器
CSS 預(yù)處理器是一個(gè)能讓你通過(guò)預(yù)處理器自己獨(dú)有的語(yǔ)法來(lái)生成CSS的程序。市面上有很多CSS預(yù)處理器可供選擇,且絕大多數(shù)CSS預(yù)處理器會(huì)增加一些原生CSS不具備的特性,例如代碼混合,嵌套選擇器,繼承選擇器等。這些特性讓CSS的結(jié)構(gòu)更加具有可讀性且易于維護(hù)。
sass

sass是誕生最早,也是世界上最成熟、最穩(wěn)定、最強(qiáng)大的專業(yè)級(jí)CSS擴(kuò)展語(yǔ)言!(官網(wǎng)說(shuō)的(O_o)?? )
sass可用使用變量,嵌套規(guī)則,混合器,繼承等編程語(yǔ)言才有的概念,代碼例子如下:
$nav-color: #F90;nav {$width: 100px;width: $width;color: $nav-color;}//編譯后nav {width: 100px;color: #F90;}
less

Less 是一門 CSS 預(yù)處理語(yǔ)言,它擴(kuò)展了 CSS 語(yǔ)言,增加了變量、Mixin、函數(shù)等特性,使 CSS 更易維護(hù)和擴(kuò)展。
代碼例子如下:
@base: #f938ab;.box-shadow(@style, @c) when (iscolor(@c)) {-webkit-box-shadow: @style @c;box-shadow: @style @c;}.box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) {.box-shadow(@style, rgba(0, 0, 0, @alpha));}.box {color: saturate(@base, 5%);border-color: lighten(@base, 30%);div { .box-shadow(0 0 5px, 30%) }}// 編譯后.box {color: #fe33ac;border-color: #fdcdea;}.box div {-webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);}
stylus

Stylus,富于表現(xiàn)力、動(dòng)態(tài)的、健壯的 CSS
代碼例子如下:
bodyfont 12px Helvetica, Arial, sans-serifa.buttonborder-radius 5px
完全不需要{} : ;的預(yù)處理器,個(gè)人是特別不喜歡這種寫法,但是對(duì)于很多喜歡簡(jiǎn)潔的開發(fā)者來(lái)說(shuō),這確實(shí)非常好的編寫方式
如魔法師一般的存在 —— CSS Houdini
有點(diǎn)時(shí)候眼看CSS出來(lái)新的屬性,但是因?yàn)闉g覽器兼容的問(wèn)題,所以往往是只能看而不能用,即便有的屬性可以用,但也因?yàn)楦鳛g覽器的現(xiàn)實(shí)情況而存在意想不到的BUG,那么這就意味著一個(gè)屬性出來(lái)之后我們要等到5年甚至更久之后才能使用嗎?都9012年了耶?
當(dāng)然不是,接下來(lái)我們可以了解一下這個(gè)如魔法師一般的存在 —— CSS Houdini
CSS Houdini是什么?
CSS Houdini是一組底層API,它們公開了CSS引擎的各個(gè)部分,從而使開發(fā)者可以通過(guò)這組API來(lái)擴(kuò)展CSS。它讓開發(fā)者擁有了直接訪問(wèn)CSSOM的能力,開發(fā)者可以通過(guò)這組API來(lái)編寫瀏覽器可解析的CSS代碼,這讓開發(fā)者可以在不需要等待瀏覽器的實(shí)現(xiàn)的前提下實(shí)現(xiàn)自己想要的CSS功能。

[圖片來(lái)自:https://www.qed42.com/blog/building-powerful-custom-properties-CSS-houdini]
如上所示,不同的API所對(duì)應(yīng)的就是瀏覽器不同的渲染環(huán)節(jié),用時(shí)下流行的概念來(lái)解釋就是瀏覽器加載時(shí)不同生命周期的鉤子函數(shù)。
簡(jiǎn)單來(lái)說(shuō),CSS Houdini就是JS IN CSS,niubility ..
CSS Houdini是怎么工作的?
我們可訪問(wèn)的7個(gè)API如下:
1.Typed OM API2.Properties & Values API3.Paint API4.Layout API5.Animation worklet6.Parser API7.Font Metrics API
Mmmm,雖然是有7個(gè)API(Houdini drafts上還有一些),但瀏覽器實(shí)際的支持情況其實(shí)是這樣的:

[圖片來(lái)自:https://ishoudinireadyyet.com/]
CSS Houdini的工作流程如下:

[圖片來(lái)自:https://www.qed42.com/blog/building-powerful-custom-properties-CSS-houdini]
1.鉤子進(jìn)入渲染的進(jìn)程中2.JS是這個(gè)鉤子的核心3.使用JS的Typed OM,可以掛載自定義的屬性,繪制圖形,布局以及動(dòng)畫4.還有其他兩個(gè)API:Parser API 和 Font Metrics API。它們用于注冊(cè)CSS相關(guān)的新事物
一些示例
本篇不打算細(xì)講CSS Houdini,所以不會(huì)畫出所有的DEMO,有興趣的可以查看底部的“資料來(lái)源”,從而獲取更加詳細(xì)的信息。
Typed OM
* {margin: 0;padding: 0;}.box {background: linear-gradient(to right, #2c3e50, #4ca1af);}

上面就是Typed OM的示例,這里值得一提的就是,如果我們用getComputedStyle去獲取transform的值,最終結(jié)果是個(gè)矩陣,這其實(shí)不太方便我們做二次操作,但是用Typed OM的JS API?computedStyleMap,去取的結(jié)果就是一個(gè)具體屬性的集合,這是非常有利于我們進(jìn)行二次操作的。
Paint API
Paint API就是允許你例如Canvas的屬性來(lái)編寫CSS樣式,使用方法也很簡(jiǎn)單,我們可以看看https://slides.iamvdo.me/waq19/#/35上的示例
首先我們新建個(gè)文件叫registerPaint.js,在里面寫下以下代碼:
registerPaint('circle-ripple', class {static get inputProperties() { return [ '--circle-color','--circle-radius', '--circle-x', '--circle-y']}paint(ctx, geom, props, args) {const x = props.get('--circle-x').value;const y = props.get('--circle-y').value;const radius = props.get('--circle-radius').value;}}
然后再新建一個(gè)index.html,并且在JS代碼里注冊(cè)上面寫好的registerPaint.js,方式如下:CSS.paintWorklet.addModule('registerPaint.js');
具體代碼如下:
.el {--circle-radius: 0;--circle-color: deepskyblue;background-image: paint(circle-ripple);}.el.animating {transition: --circle-radius 1s,--circle-color 1s;--circle-radius: 300;--circle-color: transparent;}
所以我們有以下的效果:

CSS界的Babel —— PostCSS
說(shuō)到底CSS Houdini其實(shí)也只是JS IN CSS,并不是純正的CSS,那么對(duì)于一些新的CSS屬性,我們相用的話,真的還得等5年后嗎?還有即便是有各種工具,但是像一些兼容性寫法,廠商前綴,循環(huán),原生CSS也沒(méi)有,我們不是還得需要依賴CSS預(yù)處理器嗎?
其實(shí)也不是,這時(shí)候我們可以利用CSS界的Babel —— PostCSS
PostCSS是什么?
簡(jiǎn)單來(lái)說(shuō)PostCSS就是可以讓開發(fā)者使用JS來(lái)處理CSS的處理器,它分了以下5大類功能:
增強(qiáng)代碼的可讀性
利用從 Can I Use 網(wǎng)站獲取的數(shù)據(jù)為 CSS 規(guī)則添加特定廠商的前綴。Autoprefixer[5]?自動(dòng)獲取瀏覽器的流行度和能夠支持的屬性,并根據(jù)這些數(shù)據(jù)幫你自動(dòng)為 CSS 規(guī)則添加前綴。
例如我們輸入以下代碼:
:fullscreen {}
那么就會(huì)輸出:
:-webkit-:full-screen {}:-moz-:full-screen {}:full-screen {}
將未來(lái)的 CSS 特性帶到今天!
PostCSS Preset Env[6]?幫你將現(xiàn)代 CSS 語(yǔ)法轉(zhuǎn)換成大多數(shù)瀏覽器都能理解的東西,根據(jù)你的目標(biāo)瀏覽器或運(yùn)行時(shí)環(huán)境來(lái)確定你需要的 polyfills,基于?cssdb 實(shí)現(xiàn)[7]。
例如我們輸入以下代碼:
@custom-media --med (width <= 50rem);@media (--med) {a {&:hover {color: color-mod(black alpha(54%));}}}
就會(huì)輸出:
@media (max-width: 50rem) {a:hover {color: rgba(0, 0, 0, 0.54);}}
終結(jié)全局 CSS
CSS 模塊[8]?就是說(shuō)你永遠(yuǎn)不用擔(dān)心命名太大眾化而造成沖突太普通,只要用最有意義的名字就行了。
例如我們輸入以下代碼:
.name {color: gray;}
就會(huì)輸出:
.Logo__name__SVK0g {color: gray;}
避免 CSS 代碼中的錯(cuò)誤
通過(guò)使用?stylelint 強(qiáng)化一致性約定并避免樣式表中的錯(cuò)誤[9],stylelint 是一個(gè)現(xiàn)代化 CSS 代碼檢查工具。它支持最新的 CSS 語(yǔ)法,包括類似 CSS 的語(yǔ)法,例如 SCSS 。
例如我們輸入以下代碼:
a {color: #d3;}
那么控制臺(tái)會(huì)拋出錯(cuò)誤:
app.css2:10 Invalid hex color
強(qiáng)大的網(wǎng)格系統(tǒng)
LostGrid[10]?利用 calc() 和你所定義的分割方式來(lái)創(chuàng)建網(wǎng)格系統(tǒng),無(wú)需傳遞大量參數(shù)。
例如我們輸入以下代碼:
div {lost-column: 1/3}
就會(huì)輸出:
div {width: calc(99.9% * 1/3 -(30px - 30px * 1/3));}div:nth-child(1n) {float: left;margin-right: 30px;clear: none;}div:last-child {margin-right: 0;}div:nth-child(3n) {margin-right: 0;float: right;}div:nth-child(3n + 1) {clear: both;}
可窺探的未來(lái) —— cssdb
cssdb是postcss-preset-env的實(shí)現(xiàn)基準(zhǔn),主要就是CSS的新功能功能及這些功能從提出到成為標(biāo)準(zhǔn)時(shí)所在的進(jìn)程。
cssdb跟ecma一樣,對(duì)新屬性分了不同的進(jìn)程,具體的進(jìn)程如下:
1.Stage 0:腦袋風(fēng)暴階段。高度不穩(wěn)定,可能會(huì)發(fā)生變化。2.Stage 1:實(shí)驗(yàn)階段。也非常不穩(wěn)定,可能會(huì)發(fā)生變化,但是該提案已得到W3C成員的認(rèn)可。3.Stage 2:承認(rèn)階段。高度不穩(wěn)定并且可能會(huì)發(fā)生變化,但是正在積極研究中。4.Stage3:擁抱階段。穩(wěn)定且變化不大,此功能可能會(huì)成為標(biāo)準(zhǔn)。5.Stage4:標(biāo)準(zhǔn)階段。最終的解決方案,所有主流瀏覽器都支持。
這就是postcss-preset-env依賴的實(shí)現(xiàn)基準(zhǔn),那么如果我們想要在我們的代碼里使用這些Stage,該怎么做呢?
以我的腳手架ying-template為例,我們來(lái)查看在webpack中的實(shí)際配置:
首先我們先安裝postcss以及其相應(yīng)的插件:
npm install postcss postcss-loader postcss-preset-env postcss-nesting --save-dev然后我們?cè)趙ebpack的config配置module中輸入以下配置:
module: {rules: [{test: /\.css$/,include,exclude,use: [/* 你其它的loader */ 'postcss-loader']}]}
然后在根目錄新建一個(gè)postcss.config.js
const postcssConfig = {plugins: {precss: {},'postcss-preset-env': {browsers: 'last 2 versions', // 瀏覽器兼容的版本stage: 3 // 你用的屬性所在的階段},'postcss-nesting': {} // 這里就是你所使用的插件}};module.exports = postcssConfig
這樣就完成了,如果想看完整的配置,可以clone我的腳手架:https://github.com/KRISACHAN/ying-template
(這是個(gè)多頁(yè)面的webpack4腳手架,可以使用ES6+語(yǔ)法,TypeScript,各類CSS處理器,ESLint以及單元測(cè)試等現(xiàn)代前端開發(fā)所需常用的東西,有興趣的可以去看看。)
我們可以通過(guò)https://preset-env.cssdb.org/playground這個(gè)網(wǎng)站來(lái)查看具體的編譯結(jié)果。
編譯結(jié)果圖如下:

是不是非常神奇呢?
后話
隨著前端工程的普及,某E瀏覽器的沒(méi)落,CSS的發(fā)展可謂是一日千里,近日也有一些數(shù)學(xué)屬性的提案在發(fā)起,以后會(huì)發(fā)展成什么樣,沒(méi)人可以知道。只是總的來(lái)說(shuō),CSS的未來(lái)是一片光明的。本文簡(jiǎn)單分享了一些現(xiàn)代化的CSS知識(shí),通過(guò)這些知識(shí),我們很容易就能寫出完備且現(xiàn)代化的CSS代碼,能夠給創(chuàng)造出更多的效益,希望大家可以積極地用起這些知識(shí),并對(duì)CSS可以有更多的思考以及想象。
CSS,未來(lái)可期
資料來(lái)源
1.https://developer.mozilla.org/zh-CN/docs/Archive/CSS32.http://oocss.org/3.http://getbem.com/4.http://smacss.com/5.https://sass-lang.com/6.http://lesscss.org/7.http://stylus-lang.com/8.https://blog.techbridge.cc/2017/05/23/css-houdini/9.https://www.smashingmagazine.com/2016/03/houdini-maybe-the-most-exciting-development-in-css-youve-never-heard-of/10.https://slides.iamvdo.me/waq19/fr/11.https://www.qed42.com/blog/building-powerful-custom-properties-CSS-houdini12.https://www.postcss.com.cn/13.https://cssdb.org/#staging-process14.https://s0dev0to.icopy.site/adrianbdesigns/postcss-preset-env-babel-for-css-12hp
References
[1]?KRISACHAN:?https://github.com/KRISACHAN[2]?Optional Chaining:?https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining[3]?ying-template:?https://github.com/KRISACHAN/ying-template[4]?MDN:?https://developer.mozilla.org/zh-CN/docs/Archive/CSS3[5]?Autoprefixer:?https://github.com/postcss/autoprefixer[6]?PostCSS Preset Env:?https://preset-env.cssdb.org/[7]?cssdb 實(shí)現(xiàn):?https://cssdb.org/[8]?CSS 模塊:?https://github.com/css-modules/css-modules[9]?stylelint 強(qiáng)化一致性約定并避免樣式表中的錯(cuò)誤:?http://stylelint.io/[10]?LostGrid:?https://github.com/peterramsing/lost
回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~
點(diǎn)擊“閱讀原文”查看70+篇原創(chuàng)文章
