2021 年了,是否可以拋棄 CSS 預處理器?

隨着前端工程化的不斷完善,CSS 預處理器已經成爲項目中不可缺少的部分,很多人在項目技術選型階段就會先選擇一個 CSS 預處理器,絕大部分前端工程的腳手架也內置了一系列的 CSS 預處理的模版,大家已經習慣了享受 CSS 預處理器帶來的書寫代碼的靈活流暢,反而將原生的 CSS 冷落在一旁。

可能許多前端開發人員依然覺得 CSS 預處理器屬於比較新的技術,其實不然,最早的 CSS 預處理器 Sass 從 07 年誕生距今已有 14 年,相對較新的的 Stylus 也已發佈 11 年了。

前端的技術棧發展突飛猛進,W3C 的 CSS 工作組也一直在持續從社區汲取營養,加快 CSS 的迭代。那麼到現在爲止,CSS 是否可以取代 CSS 預處理器的地位?CSS 預處理器會不會成爲 CSS 過渡時期的產物呢?

W3C 介紹—

CSS 是開放網絡的核心語言之一,由 W3C 規範 實現跨瀏覽器的標準化。因此,在深入瞭解 CSS 規範更新進度之前,我們有必要先了解一下 W3C 及其規範制定流程。

W3C 也就是萬維網聯盟(World Wide Web Consortium),又稱 W3C 理事會,是萬維網的主要國際標準組織。爲半自治非政府組織(quasi-autonomous non-governmental organisation)。由 蒂姆 · 伯納斯 - 李(Tim Berners-Lee)於 1994 年 10 月 於麻省理工學院 MIT 計算機科學與人工智能實驗室(MIT/LCS)創立。

W3C 制定了一系列標準並督促網絡應用開發者和內容提供者遵循這些標準。標準的內容包括使用語言的規範,開發中使用的導則和解釋引擎的行爲等等。W3C 制定了包括 HTML、DOM、SVG、XML 和 CSS 等的衆多影響深遠的標準規範。

W3C 規範制定流程

因爲接下來我們需要談到一些不同階段的 CSS 規範,因此這裏有必要簡單介紹一下 W3C 的規範制定流程。

按照 W3C 的 Process Document,一個推薦標準的發展需要通過不同的階段。

其中幾個主要的階段分別爲:

篇幅有限,這裏就不詳細展開講解 W3C 的標準流程了,如果想了解更多關於 W3C 標準流程與組織架構 的信息,可以查看 W3C 標準流程與組織架構。

CSS 的歷史—

HTML 和 CSS 是那麼密不可分,以至於你可能會覺得它們是一起出現的。實際上,自 1989 年 Tim Berners Lie 發明互聯網後的多年中,這個世界上都不存在一個名爲 CSS 的事物(更別說 CSS 預處理器了),Web 的原始版本根本就沒有提供一種裝飾網頁的方法。

HTML 規範雖然規定了網頁中的標題、段落應該使用的標籤,但是沒有涉及這些內容應該以何種樣式 (比如大小、位置、間距、縮進等屬性) 呈現在瀏覽器中。

不過,在隨後僅短短 10 年後,CSS 就被一個現代的 Web 社區全面採用,這期間的發生了一系列有趣的故事,有興趣的可以看一下 A Look Back at the History of CSS。

CSS1

於 1994 年,Håkon Wium Lie (哈肯 · 維姆 · 萊) 和 Bert Bos (伯特 · 波斯) 合作設計 CSS。他們在 1994 年首次在芝加哥的一次會議上第一次展示了 CSS 的建議。

1996 年 12 月 發表的 CSS1 的要求有第一版主要規定了選擇器、樣式屬性、僞類 、對象幾個大的部分。

CSS2

CSS2 在 1998 年 5 月 由 W3C 發佈,CSS2 規範是基於 CSS1 設計的,擴充和改進了很多更加強大的屬性。包括選擇器、位置模型、佈局、表格樣式、媒體類型、僞類、光標樣式。

Cascading Style Sheets Level 2 Revision 1,通常被稱爲 “CSS 2.1” ,修復了 CSS 2 中的錯誤,刪除了支持不良或不能完全互操作的特性,併爲規範增加了已經實現的瀏覽器擴展。爲了遵守 W3C 標準化技術規範的過程,CSS 2.1 在 Working Draft (WD) 狀態和 Candidate Recommendation (CP) 狀態之間來回了很多年。

CSS 2.1 於 2004 年 2 月 25 日首次成爲 Candidate Recommendation (CR) 標準,但在 2005 年 6 月 13 日又回到 Working Draft (WD) 中進行進一步審查。它於 2007 年 7 月 19 日回到 Candidate Recommendation (CP) 標準,然後在 2009 年更新了兩次。然而,由於作出了修改和澄清,它再次回到了 2010 年 12 月 7 日的 Last Call Working Draft 。

CSS3

CSS3 是層疊樣式表(Cascading Style Sheets)語言的最新版本,旨在擴展 CSS2.1。

CSS Level 2 經歷了 9 年的時間(從 2002 年 8 月到 2011 年 6 月)才達到 Recommendation(推薦) 狀態,主要原因是被一些次要特性拖了後腿。爲了加快那些已經確認沒有問題的特性的標準化速度,W3C 的 CSS Working Group  作出了一項被稱爲 Beijing doctrine 的決定,將 CSS 劃分爲許多小組件,稱之爲模塊。這些模塊彼此獨立,按照各自的進度來進行標準化。其中一些已經是 W3C Recommendation 狀態,也有一些仍是 Early Working Drafts(早期工作草案)。當新的需求被肯定後, 新的模塊也會同樣地添加進來。

image

從形式上來說,CSS3 標準自身已經不存在了。每個模塊都被獨立的標準化,現在標準 CSS 包括了修訂後的 CSS2.1 以及完整模塊對它的擴充,模塊的 level(級別)數並不一致。可以在每個時間點上爲 CSS 標準定義一個 snapshots(快照),列出 CSS 2.1 和成熟的模塊。

W3C 會定期的發佈這些 snapshots,如 2007, 2010, 2015 或 2017。

目前爲止,還沒有 level 超過 3 的模塊被標準化,未來應該會有所改變。不過有些模塊,比如 Selectors(選擇器)4 或 CSS Borders and Backgrounds(邊框和背景)Level 4 早已擁有了 Editor's Draft(編輯草案),即使它們還沒達到 First Published Working Draft(初次發佈工作草案)狀態。

有一張圖可以更加直觀的表示當前 CSS3 Modules 的分類和狀態:

問題:爲什麼沒有 CSS4?There is no single, integrated CSS4 specification, because it is split into separate "level 4" modules. Level 4 不等於 CSS 4 ,如 CSS Custom Properties for Cascading Variables Module Level 1 總不能說它是 CSS1 吧。

CSS 預處理器—

CSS 自誕生以來,基本語法和核心機制一直沒有本質上的變化,在很長一段時間內,它的發展幾乎全是表現力層面上的提升。

最開始 CSS 在網頁中的作用只是輔助性的裝飾,輕便易學是最大的需求;然而如今網站的複雜度已經不可同日而語,原生 CSS 已經讓開發者力不從心。

當一門語言的能力不足而用戶的運行環境又不支持其它選擇的時候,這門語言就會淪爲 “編譯目標” 語言。開發者將選擇另一門更高級的語言來進行開發,然後編譯到底層語言以便實際運行。於是,在前端領域,天降大任於斯人也,CSS 預處理器應運而生。

百花齊放

CSS 預處理器是一個能讓你通過預處理器自己獨有的語法來生成 CSS 的程序。

市面上有很多 CSS 預處理器可供選擇,且絕大多數 CSS 預處理器會增加一些原生 CSS 不具備或不完善的高級特性,這些特性讓 CSS 的結構更加具有可讀性且易於維護。當前社區代表的 CSS 預處理器 主要有以下幾種:

優點

雖然各種預處理器功能強大,但使用最多的,還是以下特性:變量(variables),代碼混合( mixins),嵌套(nested rules)以及 代碼模塊化 (Modules)。

接下來以 Sass 爲例,展示一下這幾個主要特性在預處理器中的實現:

Variables and Operators (+, -, *, /, %)

$font-size: 10px;
$font-family: Helvetica, sans-serif;

body {
  font: $font-size $font-family;
}

.mark {
  font-size: 1.5 * $font-size;
}

Mixins

@mixin clearfix {
  &:after {
    display: block;
    content: '';
    clear: both;
  }
}

.sidebar {
  @include clearfix;
}

Nesting

// menu

.nav {
  > li {
    > a:hover {
      background-color: red;
    }
  }
}

Modules

@import './common';
@import './github-markdown';
@import './mixin';
@import './variables';

缺點或不足

額外的編譯配置

在寫樣式前需要做一些額外的編譯配置工作,sass-node 安裝以及編譯的配置就能卡住一批前端新手。

編譯成本

每次修改代碼都需要重新編譯, 佔用時間和 CPU。

學習成本

不同的 CSS 預處理器語法不同,增加學習成本。在同一個團隊甚至項目裏,可能同時使用了好幾種樣式預處理器。

// Sass
$color: #f00;
$images: "../img";
@mixin clearfix {
  &:after {
    content: " ";
    display: block;
    clear: both;
  }
}
body {
  color: $color;
  background: url("#{images}/1.png");
  @include clearfix;
}
// Less
@color: #f00;
@images: "../img";
.clearfix() {
  &:after {
    content: " ";
    display: block;
    clear: both;
  }
}
body {
  color: @color;
  background: url("@{images}/1.png");
  .clearfix;
}

調試

在使用 CSS 預處理器時,我們通常會配置 SourceMap 來輔助調試,但即使這樣,還是會碰到一些調試困難的情況:

迴歸 CSS—

各種 CSS 預處理器在更新迭代的過程中,功能越來越繁雜花哨,但是絕大部分人用到的核心功能還是那幾樣:Variables、Mixing、Nested、Module,頂多再加上一些工具類函數。

我們既想要想要預處理器的優點,又不想要它帶來的成本和缺點,有沒有兩全其美的辦法?CSS 這麼多年一直也在從社區汲取養分加速進化和迭代,我們能不能從 CSS 標準裏面找到答案呢?

Variables in CSS

CSS 自定義屬性(CSS Custom Properties),又叫  CSS 變量(CSS Variable), 允許你自己在樣式中聲明變量,並通過 var() 函數使用。

CSS Custom Properties for Cascading Variables 規範在 2012 年 10 月首次作爲 工作草案(WD) 提出,並在 2015 年 10 月到達 候選人推薦標準(CR)階段。現在瀏覽器支持程度已經接近 93%。

CSS 變量定義及使用如下所示,可定義的類型極其豐富。

/* declaration */
--VAR_NAME: <declaration-value>;
/* usage */
var(--VAR_NAME)

/* root element selector (global scope), e.g. <html> */
:root {
  /* CSS variables declarations */
  --main-color: #ff00ff;
  --main-bg: rgb(200, 255, 255);
  --logo-border-color: rebeccapurple;

  --header-height: 68px;
  --content-padding: 10px20px;

  --base-line-height: 1.428571429;
  --transition-duration: .35s;
  --external-link: "external link";
  --margin-top: calc(2vh + 20px);
}

body {
  /* use the variable */
  color: var(--main-color);
}

不同於 SASS 預處理器變量的編譯時處理,CSS 變量是瀏覽器在運行時進行處理的,因此 CSS 變量會更加強大和靈活。

Operators

可以使用 calc() 進行計算

:root {
  --block-font-size: 1rem;
}

.block__highlight {
  /* WORKS */
  font-size: calc(var(--block-font-size)*1.5);
}

Generate Colors

可以用於通過 RGB 等函數生成和計算顏色:Generate Colors

CSS to JS

CSS 變量出現前,從 CSS 傳值給 JS 非常困難,甚至需要藉助一些 Hack 的手法。現在使用 CSS 變量,你可以直接通過 JS 獲取變量值並進行修改:

.breakpoints-data {
  --phone: 480px;
  --tablet: 800px;
}
const breakpointsData = document.querySelector('.breakpoints-data');

// GET
const phone = getComputedStyle(breakpointsData)
    .getPropertyValue('--phone');

// SET
breakpointsData.style
    .setProperty('--phone', 'custom');

Custom Theme

使用 CSS 變量,定製和動態切換網站主題非常簡單方便:

首先定義好不同主題下的變量,然後正常書寫樣式即可。

html {
  --hue: 210; /* Blue */
  --text-color-normal: hsl(var(--hue), 77%, 17%);
  ...
}
html[data-theme='dark'] {
  --text-color-normal: hsl(var(--hue), 10%, 62%);
  ...
}

通過 JS 改變元素屬性,動態切換主題:

document.documentElement.setAttribute('data-theme', 'dark')
document.documentElement.setAttribute('data-theme', 'light')

更多高級用法可以參考:CSS custom properties (native variables) In-Depth

爲什麼變量的定義以 --開頭?原因在這裏:Let's Talk about CSS Variables

Mixins in CSS

CSS 的有一個提案:CSS @apply Rule,按照該草案描述,用戶可直接使用 CSS 變量存放聲明塊,然後通過 @apply rule 使用。

:root {
    --pink-schema: {
        color: #6A8759;
        background-color: #F64778;
    }
}

body{
  @apply --pink-schema;
}

可惜這個提案已被廢棄,具體廢棄原因感興趣的可以看看這篇文章:Why I Abandoned @apply。

儘管 Mixins 現在 CSS 還沒有好的實現標準,但我們堅信遲早會有更優秀的規範湧現出來彌補 CSS 的這一塊空白。

Nesting in CSS

CSS 裏已經有 Nesting 的規範出現,儘管現在只處於 Editor’s Draft 階段:CSS Nesting Module Level 3

可以看到按照 CSS Nesting Module ,Nesting 規範基本和預處理器一模一樣。

/* Dropdown menu on hover */
ul {
  /* direct nesting (& MUST be the first part of selector)*/
  & > li {
    color: #000;

    & > ul { display: none; }

    &:hover {
      color: #f00;

      & > ul { display: block; }
    }
  }
}

Module in CSS

其實 CSS 很早就有了模塊化方案,那就是 @import,使用 CSS 的 @import 規則,可以引用其他的文件樣式。這個特性從 IE 5.5 開始就被所有的瀏覽器支持,那爲什麼一直以來使用者寥寥無幾呢,原因很多:

不過現在大家的前端項目基本都會使用構建工具(Gulp、Webpack 等)打包後再上線,因此以上那些缺點也就不存在了,而在 Webpack 的 css-loader 中,是可以配置是否開啓 @import 的。

‍‍‍‍Selector Helpers

除了上面介紹的一些主要特性,CSS 還提供了一些全新的特性來幫助你更優雅的書寫樣式。

:matches pseudo-class

已更名爲 :is()

:matches() CSS 僞類 函數將選擇器列表作爲參數,並選擇該列表中任意一個選擇器可以選擇的元素,這對於以更緊湊的形式編寫大型選擇器非常有用,而且瀏覽器支持程度也已經接近 93%。

/* 語法 */
:matches(selector[, selector]* )
.nav:matches(.side,.top) .links:matches(:hover, :focus) {
  color: #BADA55;
}

/* 相當於以下代碼 */
.nav.side.links:hover,
.nav.top.links:hover,
.nav.side.links:focus,
.nav.top.links:focus {
  color: #BADA55;
}

想要了解更多詳情可以查看規範:Selectors Level 4

@custom-selector

同時,你還可以您可以使用自定義選擇器來定義可以匹配複雜選擇器的別名。

/* 語法 */
@custom-selector: <custom-selector> <selector-list>;

定義的方式和 CSS 變量類似,使用起來稍微有點區別。

@custom-selector :--text-inputs input[type="text"],
input[type="password"];

:--text-inputs.disabled,
:--text-inputs[disabled] {
    opacity: 0.5
}

/* 相當於以下代碼 */
input[type="text"].disabled,
input[type="password"].disabled,
input[type="text"][disabled],
input[type="password"][disabled] {
    opacity: 0.5
}

用起來—

儘管上述的 CSS 特性還處於不同階段,瀏覽器的支持程度也不盡相同,但是使用 postcss-preset-env,你就可以搶先嚐試 CSS 的最新特性。

當然,postcss-preset-env 的配置也十分簡單,以 Webpack 爲例:

rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { importLoaders: 1 } },
          { loader: 'postcss-loader', options: {
            ident: 'postcss',
            plugins: () => [
              postcssPresetEnv(/* pluginOptions */)
            ]
          } }
        ]

總結—

經過一番梳理,我們發現,儘管 CSS 在社區的刺激下加快了更新迭代的速度,但是到目前爲止,依然達不到 CSS 預處理器 VS CSS 的地步,只能說在使用 CSS 預處理器的同時,也可以在項目中嘗試一些優秀的 CSS 新特性,即:CSS 預處理器 + CSS

但是我們依然堅信,在 W3C 的推動下,隨着 CSS 自身不斷完善,CSS 預處理器終究會像當年的 CoffeScriptJade 一樣,變成時代的過渡產物。到那時候,大家也就不用糾結各種 CSS 預處理器的環境配置和技術選型等,直接打開編輯器,就能愉快的書寫樣式。

參考—

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/yFubWfmCAjtMeHISLcIwRg