博客 / 詳情

返回

CSS的未來已來

  • 作者:陳大魚頭
  • github: KRISACHAN

前言

最近聽説TypeScript3.7添加了對Optional Chaining的支持,然後就想着給魚頭的腳手架ying-template的TS版本升級,然後在命令行發現這樣的一句信息:

'postcss-cssnext' 已經被 'postcss-preset-env'代替了。詳情請查看 https://moox.io/blog/deprecat...

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

淺談現代化的CSS

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

image

圖片來自[MDN]

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

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

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

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

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

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

OOCSS(Object-Oriented CSS)

OOCSS有兩個編寫原則:

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

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

image

<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.innerbottom,顧名思義就是不同位置的子盒子。

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

BEM

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

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

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

就像這樣:type-block__element_modifier

官網的例子如下:

image

<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">
    Normal button
</button>
<button class="button button--state-success">
    Success button
</button>
<button class="button button--state-danger">
    Danger button
</button>

SMACSS

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

核心只有以下6個:

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

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

官網的例子如下:

image

<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

image

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

sass可用使用變量,嵌套規則,混合器,繼承等編程語言才有的概念,代碼例子如下:

$nav-color: #F90;
nav {
  $width: 100px;
  width: $width;
  color: $nav-color;
}

//編譯後

nav {
  width: 100px;
  color: #F90;
}

less

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-DgwxMfHk-1573627304548)(http://lesscss.org/public/img...)]

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

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-tCdAdqND-1573627304550)(https://timgsa.baidu.com/timg...]

Stylus,富於表現力、動態的、健壯的 CSS

代碼例子如下:

body
  font 12px Helvetica, Arial, sans-serif

a.button
  border-radius 5px

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

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

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

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

CSS Houdini是什麼?

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

image

[圖片來自:https://www.qed42.com/blog/bu...]

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

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

CSS Houdini是怎麼工作的?

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

  1. Typed OM API
  2. Properties & Values API
  3. Paint API
  4. Layout API
  5. Animation worklet
  6. Parser API
  7. Font Metrics API

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

image

[圖片來自:https://ishoudinireadyyet.com/]

CSS Houdini的工作流程如下:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5EJvSHWB-1573627304556)(https://www.qed42.com/sites/d...]

[圖片來自:https://www.qed42.com/blog/bu...]

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

一些示例

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

Typed OM

<style>
    * {
        margin: 0;
        padding: 0;
    }
    .box {
        background: linear-gradient(to right, #2c3e50, #4ca1af);
    }
</style>
<div class="box" id="box"></div>
<script>
    'use strict'
    box.attributeStyleMap.set('width', CSS.px(200))
    box.attributeStyleMap.set('height', CSS.px(200))
    const [x, y] = 'width,height'
    .split(',')
    .map(val => Number.parseInt(box.computedStyleMap().get(val)))
    box.attributeStyleMap.set('transform', new CSSTranslate(CSS.px(x), CSS.px(y)))
    console.log(box.computedStyleMap().get('transform'))
    console.log(window.getComputedStyle(box, null)['transform'])
</script>

image

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

Paint API

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

首先我們新建個文件叫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>

所以我們有以下的效果:

image

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.css
2: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一樣,對新屬性分了不同的進程,具體的進程如下:

  1. Stage 0:腦袋風暴階段。高度不穩定,可能會發生變化。
  2. Stage 1:實驗階段。也非常不穩定,可能會發生變化,但是該提案已得到W3C成員的認可。
  3. Stage 2:承認階段。高度不穩定並且可能會發生變化,但是正在積極研究中。
  4. Stage3:擁抱階段。穩定且變化不大,此功能可能會成為標準。
  5. 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我的腳手架:https://github.com/KRISACHAN/...

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

我們可以通過https://preset-env.cssdb.org/...這個網站來查看具體的編譯結果。

編譯結果圖如下:

image

是不是非常神奇呢?

後話

隨着前端工程的普及,某E瀏覽器的沒落,CSS的發展可謂是一日千里,近日也有一些數學屬性的提案在發起,以後會發展成什麼樣,沒人可以知道。只是總的來説,CSS的未來是一片光明的。本文簡單分享了一些現代化的CSS知識,通過這些知識,我們很容易就能寫出完備且現代化的CSS代碼,能夠給創造出更多的效益,希望大家可以積極地用起這些知識,並對CSS可以有更多的思考以及想象。

CSS,未來可期

資料來源

  1. https://developer.mozilla.org...
  2. 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/20...
  9. https://www.smashingmagazine....
  10. https://slides.iamvdo.me/waq1...
  11. https://www.qed42.com/blog/bu...
  12. https://www.postcss.com.cn/
  13. https://cssdb.org/#staging-pr...
  14. https://s0dev0to.icopy.site/a...

如果你喜歡探討技術,或者對本文有任何的意見或建議,非常歡迎加魚頭微信好友一起探討,當然,魚頭也非常希望能跟你一起聊生活,聊愛好,談天説地。
魚頭的微信號是:krisChans95
也可以掃碼添加好友,備註“SF”就行
https://fish-pond-1253945200.cos.ap-guangzhou.myqcloud.com/img/base/wx-qrcode1.jpg

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.