首頁>技術>

轉自陳大魚頭

前言

最近聽說TypeScript3.7添加了對Optional Chaining的支援,然後就想著給魚頭的腳手架ying-template的TS版本升級,然後在命令列發現這樣的一句資訊:

'postcss-cssnext' 已經被 'postcss-preset-env'代替了。

其實魚頭的腳手架裡早就把postcss-cssnext換成了postcss-preset-env,不過一直沒刪,但是看到這句話之後,處於好奇,就去翻了翻PostCSS的官網,然後又思考了下這些年CSS的發展歷程,遂有這篇文章的出爐。

淺談現代化的CSS

從1997年 CSS1.0 釋出到現在,從最開始只支援簡單的文字排版到現在已經可以做出酷炫的3D動畫,CSS已經走過了22個年頭,其發展如圖所示:

隨著網際網路的發展,人們對網頁的要求已經是從只要展示圖文就好變成了各種互動跟視覺效果都需要有著更多的體驗要求。CSS為此也是不斷的更新著。

隨著web業務日益複雜化和多元化,前端開發也從單純的web page轉變成web app,在此也誕生了“前端工程化”的概念,一個完備的web app往往會很大很複雜,甚至會有很多人共同維護,以往的拼頁面,寫jQuery已經是不足以支撐現代的需求。同樣的,CSS也是如此,不再是內聯寫幾個margin,padding或者HTML一股腦引入幾個CSS就足夠的,而且由於人員配置的增多,不同的開發,命名習慣,樣式是否會衝突也是必須要考慮的。

除了工程問題,還有就是CSS與瀏覽器之間的關係也是我們不得不考慮的,雖然CSS發展的很快,但是瀏覽器對CSS新特性支援的進度確實非常緩慢的。所以雖然某些屬性已經推出了很多年,但是也往往因為瀏覽器的原因而無法進行大規模的使用。

雖然在實際開發過程中,CSS有著這樣那樣讓人無法忽略的問題,但是“方法總比困難多”,在前端界也有許多熱心的大牛們在嘗試著解決這些問題。這次讓魚頭與大家一起分享下這些與CSS相關的技巧與方法。

最初的CSS模組化 —— CSS命名規則

命名一直是開發者比較頭疼的問題,在前端裡,除了JS各種變數的命名,還有元素class的命名,雖然我們可以隨意起名,願意的話甚至可以使用.a .b .c等無意義的規則來命名,但是如果是一個長期的,大型的或多人協作的專案裡這麼命名,恐怕容易被人胖揍。這次我們來分享下業界常用的用來防捱揍的命名規則。

OOCSS(Object-Oriented CSS)

OOCSS有兩個編寫原則:

結構與樣式分離容器與內容分離

我們來看看官網的一個例子:

<div class="mod grab"> <b class="top"> <b class="tl"></b> <b class="tr"></b> </b> <div class="inner"> <div class="hd"> <h3>grab</h3> </div> <div class="bd"> <p>Body</p> </div> </div> <b class="bottom"> <b class="bl"></b> <b class="br"></b> </b> </div>

在這裡.mod是父類,所有的類都是繼承自它,.grab便是子類。

至於.top、.inner與bottom,顧名思義就是不同位置的子盒子。

這裡是以“容器”為命名法則。

BEM

BEM 是塊(Block)、 元素(Element)、修飾符( Modifier)的單詞集合。

在選擇器中,我們用以下三種符號來表示以上內容

- 中劃線 :僅作為連字元使用,表示某個塊或者某個子元素的多單詞之間的連線記號。__ 雙下劃線:雙下劃線用來連線塊和塊的子元素_ 單下劃線:單下劃線用來描述一個塊或者塊的子元素的一種狀態

就像這樣:type-block__element_modifier

官網的例子如下:

<style> .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; }</style><button class="button">\tNormal button</button><button class="button button--state-success">\tSuccess button</button><button class="button button--state-danger">\tDanger button</button>複製程式碼

SMACSS

SMACSS,一個長得很像OOCSS的規則。

核心只有以下6個:

Base:頁面的基本樣式命名規則Layout:佈局命名規則Module:模組規命名規則State:狀態命名規則Theme:主題命名規則Changing State:可變狀態的命名規則

修飾符是--,子模組是__

官網的例子如下:

<style> #header { … } #primarynav { … } #maincontent { … }</style><div id="header"></div><div id="primarynav"></div><div id="maincontent"></div>為CSS賦能 —— 前處理器

CSS 前處理器是一個能讓你通過前處理器自己獨有的語法來生成CSS的程式。市面上有很多CSS前處理器可供選擇,且絕大多數CSS前處理器會增加一些原生CSS不具備的特性,例如程式碼混合,巢狀選擇器,繼承選擇器等。這些特性讓CSS的結構更加具有可讀性且易於維護。

sass

sass是誕生最早,也是世界上最成熟、最穩定、最強大的專業級CSS擴充套件語言!(官網說的(O_o)?? )

sass可用使用變數,巢狀規則,混合器,繼承等程式語言才有的概念,程式碼例子如下:

$nav-color: #F90;nav { $width: 100px; width: $width; color: $nav-color;}//編譯後nav { width: 100px; color: #F90;}

less

Less 是一門 CSS 預處理語言,它擴充套件了 CSS 語言,增加了變數、Mixin、函式等特性,使 CSS 更易維護和擴充套件。

程式碼例子如下:

@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,富於表現力、動態的、健壯的 CSS

程式碼例子如下:

body font 12px Helvetica, Arial, sans-serifa.button border-radius 5px

完全不需要{} : ;的前處理器,個人是特別不喜歡這種寫法,但是對於很多喜歡簡潔的開發者來說,這確實非常好的編寫方式

如魔法師一般的存在 —— CSS Houdini

有點時候眼看CSS出來新的屬性,但是因為瀏覽器相容的問題,所以往往是隻能看而不能用,即便有的屬性可以用,但也因為各瀏覽器的現實情況而存在意想不到的BUG,那麼這就意味著一個屬性出來之後我們要等到5年甚至更久之後才能使用嗎?都9012年了耶?

當然不是,接下來我們可以了解一下這個如魔法師一般的存在 —— CSS Houdini

CSS Houdini是什麼?

CSS Houdini是一組底層API,它們公開了CSS引擎的各個部分,從而使開發者可以通過這組API來擴充套件CSS。它讓開發者擁有了直接訪問CSSOM的能力,開發者可以通過這組API來編寫瀏覽器可解析的CSS程式碼,這讓開發者可以在不需要等待瀏覽器的實現的前提下實現自己想要的CSS功能。

如上所示,不同的API所對應的就是瀏覽器不同的渲染環節,用時下流行的概念來解釋就是瀏覽器載入時不同生命週期的鉤子函式。

簡單來說,CSS Houdini就是JS IN CSS,niubility ..

CSS Houdini是怎麼工作的?

我們可訪問的7個API如下:

Typed OM APIProperties & Values APIPaint APILayout APIAnimation workletParser APIFont Metrics API

Mmmm,雖然是有7個API(Houdini drafts上還有一些),但瀏覽器實際的支援情況其實是這樣的:

CSS Houdini的工作流程如下:

鉤子進入渲染的程序中JS是這個鉤子的核心使用JS的Typed OM,可以掛載自定義的屬性,繪製圖形,佈局以及動畫還有其他兩個API:Parser API 和 Font Metrics API。它們用於註冊CSS相關的新事物

一些示例

本篇不打算細講CSS Houdini,所以不會畫出所有的DEMO,有興趣的可以檢視底部的“資料來源”,從而獲取更加詳細的資訊。

Typed OM

上面就是Typed OM的示例,這裡值得一提的就是,如果我們用getComputedStyle去獲取transform的值,最終結果是個矩陣,這其實不太方便我們做二次操作,但是用Typed OM的JS API computedStyleMap,去取的結果就是一個具體屬性的集合,這是非常有利於我們進行二次操作的。

Paint API

Paint API就是允許你例如Canvas的屬性來編寫CSS樣式,使用方法也很簡單,我們可以看看slides.iamvdo.me/waq19/#/35上的示例

首先我們新建個檔案叫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; }}

然後再新建一個index.html,並且在JS程式碼裡註冊上面寫好的registerPaint.js,方式如下:CSS.paintWorklet.addModule('registerPaint.js');

具體程式碼如下:

<style> .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; }</style><div class="el" id="el"></div><script> 'use strict' CSS.paintWorklet.addModule('registerPaint.js'); el.addEventListener('click', e => { el.classList.add('animating'); el.attributeStyleMap.set('--circle-x', e.offsetX); el.attributeStyleMap.set('--circle-y', e.offsetY); });</script>

所以我們有以下的效果:

CSS屆的Babel —— PostCSS

說到底CSS Houdini其實也只是JS IN CSS,並不是純正的CSS,那麼對於一些新的CSS屬性,我們相用的話,真的還得等5年後嗎?還有即便是有各種工具,但是像一些相容性寫法,廠商字首,迴圈,原生CSS也沒有,我們不是還得需要依賴CSS前處理器嗎?

其實也不是,這時候我們可以利用CSS屆的Babel —— PostCSS

PostCSS是什麼?

簡單來說PostCSS就是可以讓開發者使用JS來處理CSS的處理器,它分了以下5大類功能:

增強程式碼的可讀性

利用從 Can I Use 網站獲取的資料為 CSS 規則新增特定廠商的字首。Autoprefixer 自動獲取瀏覽器的流行度和能夠支援的屬性,並根據這些資料幫你自動為 CSS 規則新增字首。

例如我們輸入以下程式碼:

:fullscreen {}

那麼就會輸出:

:-webkit-:full-screen {}:-moz-:full-screen {}:full-screen {}

將未來的 CSS 特性帶到今天!

PostCSS Preset Env 幫你將現代 CSS 語法轉換成大多數瀏覽器都能理解的東西,根據你的目標瀏覽器或執行時環境來確定你需要的 polyfills,基於 cssdb 實現。

例如我們輸入以下程式碼:

@custom-media --med (width <= 50rem);@media (--med) { a { &:hover { color: color-mod(black alpha(54%)); } }}

就會輸出:

@media (max-width: 50rem) { a:hover { color: rgba(0, 0, 0, 0.54); }}

終結全域性 CSS

CSS 模組 就是說你永遠不用擔心命名太福斯化而造成衝突太普通,只要用最有意義的名字就行了。

例如我們輸入以下程式碼:

.name { color: gray;}複製程式碼

就會輸出:

.Logo__name__SVK0g { color: gray;}

避免 CSS 程式碼中的錯誤

通過使用 stylelint 強化一致性約定並避免樣式表中的錯誤,stylelint 是一個現代化 CSS 程式碼檢查工具。它支援最新的 CSS 語法,包括類似 CSS 的語法,例如 SCSS 。

例如我們輸入以下程式碼:

a { color: #d3;}

那麼控制檯會丟擲錯誤:

app.css2:10 Invalid hex color

強大的網格系統

LostGrid 利用 calc() 和你所定義的分割方式來建立網格系統,無需傳遞大量引數。

例如我們輸入以下程式碼:

div { lost-column: 1/3 }

就會輸出:

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; }

可窺探的未來 —— cssdb

cssdb是postcss-preset-env的實現基準,主要就是CSS的新功能功能及這些功能從提出到成為標準時所在的程序。

cssdb跟ecma一樣,對新屬性分了不同的程序,具體的程序如下:

Stage 0:腦袋風暴階段。高度不穩定,可能會發生變化。Stage 1:實驗階段。也非常不穩定,可能會發生變化,但是該提案已得到W3C成員的認可。Stage 2:承認階段。高度不穩定並且可能會發生變化,但是正在積極研究中。Stage3:擁抱階段。穩定且變化不大,此功能可能會成為標準。Stage4:標準階段。最終的解決方案,所有主流瀏覽器都支援。

這就是postcss-preset-env依賴的實現基準,那麼如果我們想要在我們的程式碼裡使用這些Stage,該怎麼做呢?

以我的腳手架ying-template為例,我們來檢視在webpack中的實際配置:

首先我們先安裝postcss以及其相應的外掛:

npm install postcss postcss-loader postcss-preset-env postcss-nesting --save-dev

然後我們在webpack的config配置module中輸入以下配置:

module: { rules: [ { test: /\\.css$/, include, exclude, use: [/* 你其它的loader */ 'postcss-loader'] } ]}

然後在根目錄新建一個postcss.config.js

const postcssConfig = { plugins: { precss: {}, 'postcss-preset-env': { browsers: 'last 2 versions', // 瀏覽器相容的版本 stage: 3 // 你用的屬性所在的階段 }, 'postcss-nesting': {} // 這裡就是你所使用的外掛 }};module.exports = postcssConfig

這樣就完成了,如果想看完整的配置,可以clone我的腳手架:github.com/KRISACHAN/y…

(這是個多頁面的webpack4腳手架,集成了babel 7,precss 4,typescript3.7,karma以及eslint等現代前端開發所需常用的東西,有興趣的可以去看看。)

我們可以通過preset-env.cssdb.org/playground這個網站來檢視具體的編譯結果。

編譯結果圖如下:

  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 「乾貨」帶你認識 flask 郵件傳送