整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          CSS的革命:從基礎到前沿的全面演變

          離 HTML

          大家都知道 CSS 在 HTML 里面的幾種寫法,先稍微回顧一下:

          • 行內樣式:style 屬性
          <element style="style-name: style-value;" />

          內聯樣式:<style> 標簽內部編寫 CSS

          <style>
          element {
              style-name: style-value;
          }
          .class-name {
              style-name: style-value;
          }
          #element-1 {
              style-name: style-value;
          }
          </style>
          
          <element id="element-1" class="class-name" />

          導入樣式:<style> 標簽內部使用 @import 引入外部 CSS 文件

          <style>
          @import url("style-1.css");
          @import "style-2.css";
          </style>
          
          <element id="element-1" class="class-name" />

          外部樣式:<meta> 標簽內部使用 <link> 標簽引入外部 CSS 文件

          <header>
              <meta>
                  <link type="text/css" rel="stylesheet" href="css-herf">
              </meta>
          </header>
          
          <body>
              <element id="element-1" class="class-name" />
          </body>

          在直接編寫 HTML 的時候,我們幾乎不會用行內樣式與導入樣式。行內樣式可復用性、可維護性、性能都很差,而導入樣式看起來雖然挺好的,但是有著致命缺點,我們一般不會使用:會影響瀏覽器 并發 請求資源的效率,因為它們的加載時機不確定或會被其他資源阻塞。 具體表現如下:

          執行順序

          內聯 @import

          外部 @import

          標簽

          內聯 @import

          并行

          外部 @import阻塞

          并行,部分情況阻塞(IE)

          外部 @import

          /

          并行

          @import 阻塞

          標簽

          /

          /

          并行

          可以發現其優先級在大部分情況下低于標簽,最好的情況也只是并行渲染,這導致:

          • 可能會延遲頁面樣式的渲染,可能會造成頁面閃爍的現象;
          • 可能會延遲頁面樣式的應用,可能會和頁面中的 javascript 腳本產生沖突,導致 js 修改的樣式被后加載的外部樣式表覆蓋;

          在最開始的時候,我們在努力讓 CSS 與 HTML 分離,形成獨立的兩個文件,這樣有利于內容與表現的分離。

          組織 CSS 代碼

          在實際開發中我們發現,使用 class 選擇器比使用其他選擇器優勢更大,因為class選擇器是最初的模塊化思想。但是隨著項目規模的增大,CSS 的一個缺點逐漸浮現出來:全局作用域,這導致我們不得不將這些 class 的命名區別開來。為了解決 CSS 全局作用域和命名沖突的問題,很多大佬、組織提出了一些規范和指南。

          如何組織 CSS 代碼?這些方法有些是基于面向對象思想的,有些是基于組件化思想的,有些是基于樣式規則分類的,有些是基于樣式規則分層的,還有些是基于最小化樣式規則的。不同的方法論有不同的特點和適用場景。

          當然,我們現在已經不會去直接使用這些方法了,但了解這些方法也有助于我們一窺大佬們的思想。

          下面我們來具體的看一下每種方法。

          OOCSS - Object Oriented CSS

          OOP 的主要要素:類(方法、變量的集合)、對象(類的一個實例) 類與類的關系有繼承(父子關系)、實現(類型-定義關系)、依賴(平等關系)、關聯(平等關系)、聚合(弱部分-整體關系)、組合(強部分-整體關系)

          我們肯定都聽說過 OOP(Object-oriented programming - 面對對象編程),那么 OOCSS 同理,是一種面對對象的 CSS 命名方式,它于 2008 年由 Nicole Sullivan 提出。

          CSS 本身是聲明式的編程語言,不具備任何的面對對象能力,我們需要構建出自己的一套“約定”。在 OOCSS 中,“對象”指 HTML 元素或相關內容(如 CSS 類或 JavaScript 方法)。比如,側邊欄小部件對象可復制用于不同目的(通訊注冊、廣告塊、最近文章等)。CSS “對象”是一種重復的視覺模式,可以抽象為獨立的代碼片段。 OOCSS 主要有以下兩個原則:

          結構和皮膚分離

          • 結構是指應用于元素(寬度、高度、邊距、填充)的不可見樣式,而皮膚是可見樣式(顏色、字體、陰影)。
          • 用可重復的類來定義獨特的樣式(例如浮動,clearfix,獨特的字體堆棧)。
          // non-OOCSS
          .button {
             width: 100px;
             height: 50px;
             background: #000;
             color: #fff;
          }
          
          
          .button-2 {
             width: 100px;
             height: 50px;
             background: #fff;
             color: #333;
          }


          // OOCSS
          .button {
             background: #000;
             color: #fff;
          }
          
          .button-2 {
             background: #fff;
             color: #333;
          }
          
          .btn-structure {
             width: 100px;
             height: 50px;
          }

          容器和內容分離

          • 內容指的是圖片、段落、div等元素,它們被嵌套在作為容器的其他元素中。
          • 避免使用子選擇器和 ID 選擇器,用于內容元素的樣式應該是獨立于容器類的,這樣它就可以在其任何地方不受限制地使用。
          // non-OOCSS
          #sidebar {
              padding: 2px;
              left: 0;
              margin: 3px;
              position: absolute;
              width: 140px;
          }
          
          #sidebar .list {
              margin: 3px;
          }
          
          #sidebar .list .list-header {
              font-size: 16px;
              color: red;
          }
          
          #sidebar .list .list-body {
              font-size: 12px;
              color: #FFF;
              background-color: red;
          }


          // OOCSS
          .sidebar {
              padding: 2px;
              left: 0;
              margin: 3px;
              position: absolute;
              width: 140px;
          }
          
          .list {
              margin: 3px;
          }
          
          .list-header {
              font-size: 16px;
              color: red
          }
          
          .list-body {
              font-size: 12px;
              color: #FFF;
              background-color: red;
          }

          許多開發者認為,OOCSS 易于分享和維護。相比之下,SMACSS 等模塊化方法對 CSS 對象有更嚴格的分類規則。

          SMACSS - Scalable and Modular Architecture for CSS

          SMACSS(Scalable and Modular Architecture for CSS)即可伸縮及模塊化的 CSS 結構,由 Jonathan Snook 在 2011 年雅虎時提出。與 OOCSS 不同的是,SMACSS 的關注點在于網絡元素的所屬功能。

          SMACSS 將網頁的 CSS 分為以下幾個組件大類:

          Base(基礎)

          顧名思義,基本規則需要應用于網頁的基本元素。下面的示例可以被認為是 SMACSS 中的一部分基本規則:

          body { 
              margin-left : 20px; 
          }
          
          p {
              font-family: xyz;
          }

          我們將基本規則應用于在整個網頁中保持一致的元素。在上面的 SMACSS 示例中,我們希望內容距離左邊 20px 顯示,段落元素應該有一種特定的字體。

          除了直接元素外,Base 類型的 CSS 還可以使用后代選擇器、子選擇器和偽類。但是,在創建基本規則時,我們不能使用任何 !important。這可能是因為當我們的樣式從不同的部分或特異性問題(稍后討論)開始覆蓋時,會顯示出不希望的行為。

          你可能會想到使用 CSS-resets 來代替這些樣式,但是這會增加從服務器發送到客戶端的代碼量。因此,如果你想要創建任何默認設置的 CSS,Base 是一個很好的地方來記下它們。

          Layout(布局)

          第二條規則講的是如何設計網頁應用的布局的 CSS。網頁的主要部分都屬于布局的范疇。為它們設計CSS通常會遇到很多挑戰,因為涉及到很多元素,而且用多個 ID 來定義每個布局會讓事情變得更復雜。

          一個簡單的不太成熟的 CSS 設計如下:

          #header, #features, #sidebar {
              //樣式
          }

          但是,當我們需要根據不同的偏好來設計多種布局時,上面的不太成熟的CSS設計就會失效。在這種情況下,可以用前綴“l”來表示這個類選擇器是基于一個布局元素的。

          #header {
              //樣式
          }
          
          #sidebar {
              //樣式
          }
          
          .l-mobile #sidebar {
              //移動端特定的樣式,比如寬度
          }

          在上面的示例中,l-mobile 類表示它是為了改變與移動端相關的元素的“布局”而構建的。因此,“l”這個名稱在 SMACSS 的布局規則中并不是必須使用的。但是,SMACSS 作者建議使用它作為一個標準,以便更好地閱讀。

          題外話:我不同意 SMACSS 的這種在 layout 類型里面用 ID 選擇器的行為,我們應該從始至終使用 class 選擇器。如下示例

          .l-header {
              //樣式
          }
          
          .l-sidebar {
              //樣式
          }
          
          .l-mobile-sidebar {
              //移動端特定的樣式,比如寬度
          }

          Module(模塊)

          模塊是布局元素的較小部分,例如導航、小部件、對話框等。將模塊視為布局的一部分會增加不必要的復雜性,因為模塊在多個地方使用比大型布局更多,可以把布局看作是主要的布局,模塊看作是次要的布局

          模塊的命名很符合我們的直覺:

          .heading {}
          
          .heading-email {}
          
          .heading-news {}

          State(狀態)

          在我們精心制定了布局和模塊規則之后,我們還需要考慮元素狀態的設計。當一個元素有多個狀態時,就需要應用狀態規則。例如,一個模塊可以處于錯誤狀態(取決于收到的錯誤)或成功狀態。對于這兩種狀態,模塊都需要渲染不同的樣式,這就是狀態規則的作用。

          .is-error {
              //樣式
          }
          
          .is-success {
              //樣式
          }

          Theme(主題)

          主題規則是為 Web 應用程序的主題定義的。例如,每個網站都有一個反映業務或基于其他策略的主題。但很多網站可能不需要更換主題的功能,因此這個規則是可選的。

          .button-large {
              width: 60px;
              height: 60px;
          }

          這種規則看起來和默認規則很像,但基本規則只針對默認的外觀,而且往往是類似于重設為默認的瀏覽器設置;而主題規則則更像是一種風格設計,它給出了最終的外觀,對于這個特定的色彩方案是獨一無二的。

          BEM - Block Element Modifier


          BEM(Block Element Modifier)是一種典型的 CSS 命名方法論,由 Yandex 團隊在 2009 年前提出。BEM 和上面兩種方法非常的不一樣,他是通過全局統一的格式來命名出獨一無二的 class,每一個 class 都由以下部分組成:

          Block

          獨立的實體,其本身就有著明確意義。比如header, container, menu, checkbox, input等。

          Element

          一個區塊的一部分,沒有獨立的意義,在語義上與它的區塊相聯系。就是說 Element 不能脫離 Block 存在。比如menu-item, list-item, checkbox-caption, header-title等。如果 Element 里面還有 Element,使用 - 分割。

          Modifier

          塊或元素上的一個標志。用它們來改變外觀或行為,類似于 SMACSS 的 State + Theme。比如disabled, highlighted, checked, fixed, size-big, color-yellow 等。

          BEM 的規則非常清晰易懂,而且可以使用 SCSS 等預處理器來完成,在 2020 年的 CSS 調查里面位居榜首。

          // 配合 SCSS 語法
          .card {
            &__head {}
            &__menu {
              &-item {
                &--active {}
                &--disable {}
              }
            }
            &__body {}
            &__foot {}
          }

          ITCSS - Inverted Triangle CSS

          作為原子化 CSS 的思路來源,ITCSS 是一種比較新的 CSS 代碼組織方法。他 CSS 命名無關,可以與 BEM、SMACSS 或 OOCSS 等方法一起使用。ITCSS 把 CSS 代碼的特征分成了三個維度,再根據這三個維度進行分層:

          1. Reach - 范圍:CSS 代碼所能影響的范圍
          2. Specificity - 特異性:CSS 代碼的普適程度
          3. Explicitness - 明確性:CSS 代碼的名稱確定性

          根據以上特征的不同,ITCSS 將 CSS 代碼分為以下幾層:

          • Settings 設置 -- 與預處理器一起使用,包含字體、顏色定義等。
          • Tools 工具 -- 全局使用的混合元素和函數。重要的是不要在前兩層輸出任何CSS。
          • Generic 通用 -- 重置和/或規范化樣式,盒狀大小的定義,等等。這是產生實際CSS的第一層。
          • Elements 元素 -- 裸露的HTML元素的樣式(如H1、A等)。這些元素帶有瀏覽器的默認樣式,所以我們可以在這里重新定義它們。
          • Objects 對象 -- 基于類的選擇器,它定義了非裝飾的設計模式,例如OOCSS中的媒體對象。
          • Components 組件 -- 特定的UI組件。這是我們大部分工作發生的地方。我們經常將UI組件由Objects和Components組成。
          • Utilities 實用工具 -- 實用工具和輔助類,能夠覆蓋三角形中的任何東西,例如,隱藏輔助類。

          可以看到,ITCSS 的分層較多,每層的樣式都可以覆蓋前面一層的樣式。將 ITCSS 層組織到子文件夾中,并使用 Sass 或其他預處理器編譯新添加的文件:

          // ITCSS + SCSS
          @import 'settings/*';
          @import 'tools/*';
          @import 'generic/*';
          @import 'elements/*';
          @import 'vendor/*';
          @import 'objects/*';
          @import 'components/*';
          @import 'utilities/*';


          // BEMIT
          .s-name
          .t-name
          .g-name
          .e-name
          .v-name
          .o-name
          .c-name
          .u-name

          ITCSS 只是一種組織結構,這種組織結構清晰易懂,我們可以快速組織并分享我們的 CSS 代碼。ITCSS 和 BEM 一起使用的時候,有一個獨特的名稱:BEMIT,使用的時候無需去思考 CSS 代碼存放的位置,只需要在 BEM 前面加上 ITCSS 的前綴即可。

          pre-processor


          上面我們提到了“預處理器”,可能你會疑問,預處理器是什么東西?

          CSS 預處理器是一種程序,可讓您從預處理器自己的獨特語法生成 CSS。簡單來說,預處理器就是一個編譯器,有了這個編譯器,我們就可以用很多原生 CSS 不具有的特性,例如 mixin、嵌套選擇器、繼承選擇器等。這些特性使 CSS 結構更具可讀性和更易于維護。

          每個 CSS 預處理器都有自己的語法,它們編譯成常規 CSS,以便瀏覽器可以在客戶端呈現它。CSS 預處理器以或多或少不同的方式做類似的事情,并且每個都有自己的語法和生態系統(工具、框架、庫)。現在流行的 CSS 預處理器大概有以下三個:

          Sass & SCSS: Syntactically Awesome Style Sheets

          Sass 是最流行和最古老的 CSS 預處理器,最初發布于2006年。它的創造者 Natalie Weizenbaum 和 Hampton Catlin 受到 Haml模板語言的啟發,該語言為 HTML 增加了動態功能。他們的目標是在 CSS 中也實現類似的動態功能。因此,他們想出了一個 CSS 預處理器,并將其命名為 Syntactically Awesome Style Sheets。

          Sass 預處理器允許我們使用變量、if/else 語句、for/while/each 循環、繼承、運算符、插值、混合器和其他動態功能,然后將代碼編譯成網絡瀏覽器可以解釋的普通CSS。

          Sass 有兩種語法。

          • .sass 文件擴展名使用基于縮進的舊語法。
          • SCSS 是 Sass 3 引入新的語法,是 Sassy CSS 的簡寫,是更新和更廣泛使用的語法,使用 .scss 文件擴展名。

          下面我們來看個例子,可以看到 SCSS 語法更類似 CSS 語法,沒有什么上手難度:

          /* Sass */
          $primary-color: seashell $primary-bg: darkslategrey  body
              color: $primary-color     background: $primary-bg
              


          /* SCSS */ $primary-color: seashell; $primary-bg: darkslategrey;  body {     color: $primary-color;     background: $primary-bg; }

          Sass 提供了一些很方便我們編寫 CSS 的機制,比如 mixin 函數:

          @mixin card($width, $height, $bg, $border) {       width: $width;       height: $height;       background: $bg;       border: $border; }
          
          .card-1 {
              @include card(300px, 200px, yellow, red 2px solid);
          }
          .card-2 {
              @include card(400px, 300px, lightblue, black 1px dotted);
          }

          其他機制包括:

          • 變量作用域機制
          $global-variable: global value;
          
          .content {
              $local-variable: local value;
              global: $global-variable;
              local: $local-variable;
          }
          
          .sidebar {
              global: $global-variable; 
              // This would fail, because $local-variable isn't in scope:
              // local: $local-variable;
          }
          

          @extend -- CSS class 繼承

          .error {
              border: 1px #f00;
              background-color: #fdd;
              &--serious {
                  @extend .error;
                  border-width: 3px;
              }
          }
          
          // equal to
          .error, .error--serious {
              border: 1px #f00;
              background-color: #fdd;
          }
          .error--serious {
              border-width: 3px;
          }
          • 嵌套語法
          • @if @else @for @while 等條件循環控制語句
          $base-color: #036;
          @for $i from 1 through 3 {
              ul:nth-child(3n + #{$i}) {
                  background-color: lighten($base-color, $i * 5%);
              }
          }
          • @import 模塊化

          還有其他很多詳細的語法,大家可以去官方文檔了解閱讀,這里不多贅述。

          LESS: "Leaner Style Sheets"

          LESS 由 Alexis Sellier 在 Sass 之后的 2009 年發布,LESS 受 Sass 影響很多,但其本身也影響了 SCSS。之后 Bootstrap 決定從 LESS 轉移到 Sass,這對 LESS 的普及是一個巨大的打擊,導致目前來說 SCSS 的普及率高過 LESS 非常多。

          LESS 的語法非常像 SCSS,但其邏輯處理能力較弱,有興趣的小伙伴可以去官網文檔了解。

          Stylus: Expressive, dynamic, and robust CSS

          Stylus 的第一個版本是在 LESS 一年后推出的,由前 Node.js 開發者 TJ Holowaychuk 在2010年推出。Stylus 結合了Sass 強大的邏輯能力和 LESS 簡單明了的設置,讓他在預處理器的市場份額有著一席之地。

          當然雖然 Stylus 語法靈活,支持兩種不同的語法,但是這也帶來了容易導致混亂的缺點,而且大部分人都選擇用 Sass,導致目前遠不如 Sass 的影響力。


          我們可以很清晰的看到,在SCSS 推出 + node-sass 在重構為 dart-sass 之后,爆發式增長,LESS 和 stylus 都沒有什么抵抗的力量。如果目前需要選擇一個預處理器,這里推薦使用 Sass 的 SCSS 語法。

          不過不管這些 CSS 預處理器流行度怎么樣,他們都是能幫助我們快速組織編寫 CSS 代碼的工具,因地制宜即可。甚至后面我們就知道了,我們也不是非要用他們不可。

          PostCSS


          好了經過上面的部分,我猜你對 Less、Sass 和 Stylus 等預處理程序已經很熟悉。這些工具是當今網絡開發生態系統的重要組成部分。但是,傳統的預處理器有幾個問題:

          • 它們不遵循 CSS 標準。每個預處理器都已經有了自己的標準。遺憾的是,它們不以與 W3C 標準兼容為目標,這意味著它們不能把它們的功能作為 polyfills,用于早期測試較新的 W3C 標準。
          • 它們是不可擴展的。無論你選擇哪種預處理器,你都被限制在它所提供的功能集上。如果你需要在此基礎上的任何功能,你需要在構建過程中單獨添加。如果你想寫你的擴展,你就得靠自己了。

          可以看到,盡管傳統的預處理器帶來了許多很優秀的特性,但他們嚴格限制了我們的 CSS 編寫思路。這就給了 PostCSS 用武之地。

          上面這幅圖是 PostCSS 的原理,很簡單地,你可以把 PostCSS 理解成 Babel 一樣的代碼轉換工具,將我們編寫的 CSS 轉成可以瀏覽器可以直接識別的 CSS,而預處理器相當于 Typescript。PostCSS 接收一個 CSS 文件并提供了一個 API 來分析、修改它的規則(通過把 CSS 規則轉換成一個 抽象語法樹 的方式)。在這之后,這個 API 便可被許多 插件 利用來做有用的事情,比如尋錯或自動添加 CSS vendor 前綴。


          可以看到,PostCSS 的下載次數遠超預處理器之和,這是因為很多很多三方庫都基于 PostCSS 的能力來構建。同時 PostCSS 擁有眾多插件,比如:Autoprefixer(前綴添加)、lost(基于 calc 的柵格系統)、Stylelint(CSS 格式檢查)、CSSNext(使用瀏覽器未支持的 CSS語法) 等,這些優秀的插件都基于 PostCSS 的能力。

          了解完這些之后,我們就會明白,我們其實沒有必要糾結使用哪些預處理器了,只需要在 PostCSS 里面安裝我們想要的預處理器語法插件就行。

          高級模塊化

          其實上面我們介紹的組織 CSS 代碼都是具有模塊化思想的,但是這些模塊化思想都是非自動化的,說白了就是,我們需要時時刻刻記住這些約定,這對于我們的開發效率來說影響甚大。隨著 React 框架的火熱,前端代碼模塊不再遵循“關注點分離”原則,而是一個 HTML+CSS+JS 構成的組件為核心。但是 React 并沒有像 Vue 一樣提供了 scope 等內置機制,因為 React 本身的設計原則決定了其不會提供原生的 CSS 封裝方案。舊的代碼模塊化約定已經不能適應新的開發方式的需求,模塊化的兩種最強形式呼之欲出。

          CSS Modules

          首先是對我們代碼編寫方式影響較小的 CSS Modules。其實 CSS Modules 在本質上也是 CSS-in-JS 的一種,但其功能比較簡單,也類似于關注點分離的寫法,所以其從 CSS-in-JS 中獨立出來并成為一種獨立的思想。

          CSS Modules 的核心很簡單,就是只管理好 CSS 代碼的作用域,讓我們可以通過類似 ESM 的方式來組織 CSS 代碼。他以 CSS 文件模塊為單元,將模塊內的選擇器附上特殊的哈希字符串,以實現樣式的局部作用域。對于大多數 React 項目來說,這種方案已經足夠用了。


          CSS Modules 有以下幾個重要特性:

          • 局部作用域:構建時會將類名style.title編譯成一個哈希字符串。可以在對應的插件配置中定制哈希類名。
          // before
          .title {
              color: red;
          }
          
          // equal to
          :local(.title) {
              color: red;
          }
          
          // after
          ._3zyde4l1yATCOkgn-DBWEL {
              color: red;
          }
          • 全局作用域
            • :global(.className),使用這種語法的類名不會被編譯成哈希字符串。
          • 類名組合機制:一個選擇器可以繼承另一個選擇器的規則
            • 編譯前:
          .className {
              background-color: blue;
          }
          
          .title {
              composes: className;
              color: red;
          }


          <h1 className={style.title}>

          編譯后:

          ._2DHwuiHWMnKTOYG45T0x34 {
              color: red;
          }
          
          ._10B-buq6_BEOTOl9urIjf8 {
              background-color: blue;
          }


          <h1 class="_2DHwuiHWMnKTOYG45T0x34 _10B-buq6_BEOTOl9urIjf8">

          使用 CSS Modules 之后,我們不需要使用任何類似 BEM 的命名約定了,因為我們的 CSS 代碼的作用域已經被分隔開了,不要命名一個項目唯一的 CSS 類名。同時我們可以在項目的組件文件夾中直接編寫 CSS 文件,這對于我們的開發體驗來說是一個質的飛躍。

          CSS Modules 可以配合 PostCSS 一起使用,這樣我們也可以用各種額外特性,例如 CSS 變量或者預處理器語法等。

          CSS-in-JS

          CSS-in-JS 在 2014 年由 Facebook 的員工 Vjeux 在 NationJS 會議上提出:可以借用 JS 解決許多 CSS 本身的一些“缺陷”,比如全局作用域、死代碼移除、生效順序依賴于樣式加載順序、常量共享等等問題。


          很明顯,CSS-in-JS 是一種沒有標準規范的思想,他的主要要義就是在 JS 里面寫 CSS。這就導致其實現非常非常多,目前有六十多種 CSS-in-JS 的實現。每隔一段時間,都會有新的語法方案或實現,嘗試補充、增強或是修復已有實現。


          CSS-in-JS 雖然解決了一些直接編寫 CSS 代碼的問題,但他也帶來了一些問題:

          • 使用 CIJ 可能是一種不必要的需求。如果開發者能夠充分理解 CSS 的基本概念,比如特異性、級聯、繼承等,同時運用一些預處理或后處理工具(例如 scss/postcss)和規范化的命名方法(例如 BEM),那么純 CSS 就可以滿足開發需求,無需引入額外的復雜度。
          • CIJ 的方案和工具琳瑯滿目,但是缺乏統一的標準和規范,許多還處于試驗性或不穩定的階段,使用起來存在較大的風險和不確定性。一旦選擇了某個方案,就可能面臨這個方案被廢棄或不兼容的問題,導致代碼難以維護或遷移。
          • CIJ 會增加運行時的性能開銷,因為它需要在瀏覽器中動態生成和注入 CSS,這會消耗更多的內存和 CPU 資源,影響頁面的加載速度和用戶體驗。

          下面我們來看看幾種 CIJ 的具體實現。

          styled-components


          styled-components 是一種 CSS-in-JS 的實現方式,它可以讓你在 React 組件中直接寫 CSS 代碼,從而實現組件和樣式的一一對應。這樣,你就不需要再為每個組件定義一個單獨的 CSS 文件或者使用類名來管理樣式了,而是可以將樣式和組件的邏輯和結構緊密地結合在一起。

          styled-components 的原理是利用了 JavaScript 的標簽模板字符串(tagged template literals)功能,將 CSS 代碼作為一個函數的參數傳遞,然后在運行時動態生成和注入樣式表。例如,你可以這樣定義一個按鈕組件:

          import styled from 'styled-components';
          
          const Button = styled.button`
            background: palevioletred;
            color: white;
            border-radius: 4px;
          `;

          這里,styled.button 是一個函數,它接收一個模板字符串作為參數,并返回一個 React 組件。這個組件會渲染一個帶有指定樣式的按鈕元素。你可以像使用任何其他 React 組件一樣使用這個 Button 組件:

          <Button>Click me</Button>

          styled-components 有以下幾個優勢:

          • 可以避免 CSS 類名的沖突和全局污染,因為它會自動生成唯一的類名。這樣,你就不需要擔心命名沖突或者覆蓋了其他組件的樣式了。
          • 可以利用 JavaScript 的變量和邏輯來動態地控制樣式。例如,你可以根據 props 或者主題來改變組件的顏色、大小、邊距等屬性。
          • 可以支持主題和樣式繼承等功能,方便實現 UI 的一致性。例如,你可以使用 ThemeProvider 組件來提供一個全局的主題對象,然后在任何組件中通過 props.theme 來訪問它。

          styled-components 也有以下幾個劣勢:

          • 可能會增加代碼的復雜度和可讀性,因為需要在 JavaScript 中混合寫 CSS 代碼。這樣,你就不能利用一些專門針對 CSS 的工具或者編輯器功能了,而且也可能降低代碼的可維護性和可測試性。
          • 可能會影響運行時的性能,因為需要在瀏覽器中解析和注入樣式表。這樣,你就不能利用一些針對 CSS 的優化技術了,比如 CSS 提取、壓縮、緩存等。
          • 可能會導致樣式的重復或冗余,因為每個組件都會生成一個獨立的樣式表。這樣,你就不能利用 CSS 的繼承和級聯機制了,而且也可能增加最終打包后的文件大小。
          • 可能會與一些第三方庫或工具不兼容,比如 CSS 模塊、CSS 提取、CSS Lint 等。這樣,你就不能使用這些庫或工具來提高你的開發效率和代碼質量了。

          總之,styled-components 可以讓你在 React 組件中直接寫 CSS 代碼,從而實現組件和樣式的一一對應。如果你想使用 styled-components 來開發你的 React 應用,你需要根據你的具體需求和場景來權衡它的利弊,選擇適合你的方案。

          Emotion

          Emotion 是另外一個流行的 CSS-in-JS 庫,由于這兩個庫比較類似,我們主要就針對這兩個庫做一些對比:

          • styled-components 和 emotion 都使用了模板字符串(template literals)來創建樣式化的組件,這樣可以保持CSS的語法和高亮,同時也可以使用JavaScript的變量和表達式。
          • styled-components 和 emotion 都支持主題(themes),即一組全局的樣式變量,可以在不同的組件中共享和使用。它們也都支持媒體查詢(media queries),即根據不同的設備或屏幕尺寸來調整樣式。
          • styled-components和emotion的主要區別在于:
            • emotion支持更多的API,例如css prop,@emotion/core,@emotion/styled等,而styled-components只支持styled API。可以說這點是差距最大的一點。
            • emotion提供了更好的開發者體驗,例如支持自動標簽(auto-labels),即在開發者工具中顯示組件的名稱,以及支持源映射(source maps),即在開發者工具中顯示樣式的來源文件和行數。
            • emotion具有更高的渲染速度,即在瀏覽器中將樣式應用到組件上所需的時間。根據測試結果,emotion比styled-components 快了約10%。

          最終我們還是需要根據自己的項目需求和偏好來選擇合適的庫。如果項目需要更多的靈活性和性能,可以選擇 emotion。如果項目需要更簡單和一致的API,可以選擇 styled-components。

          Stitches & vanilla-extract

          下面介紹的是兩個比較新且評價較好的的 CSS-in-JS 庫。


          Stitches **是一個 TypeScript 友好的 CSS-in-JS 庫,具有接近零運行時,服務器端渲染,多變量支持和一流的開發人員體驗。vanilla-extract 是一個 Stitches 的競爭對手,稱自己為“CSS Modules-in-TypeScript”,vanilla-extract 是真正的零運行時(Stitches 6 kB gzipped)。

          類似 styled-components 的 CSS-in-JS 庫由于需要在運行時動態注入 CSS,性能較差,而新的 CSS-in-JS 庫基本都拋棄了運行時的思路,轉而在編譯階段生成固定的 CSS 代碼。這樣有兩個好處:

          可以減小 JS 文件的體積 可以完美支持 SSR 加快客戶端運行速度

          具體到他們的區別,可以看看這篇文章:Vanilla-Extract & Stitches: A Comparison,總之,vanilla-extract 是最為先進、迅猛的 CSS-in-JS 庫,因為他是真正的零運行時庫,FCP 可能稍大,但 TTL 很小,用戶體驗很棒,適合開發大型應用。 我們來簡單看個 vanilla-extract 的 demo:

          import { style } from '@vanilla-extract/css';
          
          export const parentClass = style({
              background: 'red',
              ':hover': {
                  background: 'blue',
              },
          });
          
          export const childClass = style({
              selectors: {
                  '&:nth-child(2n)': {
                      background: '#fafafa',
                  },
                  [`${parentClass} &`]: {
                      color: 'pink',
                  },
              },
          });


          import { childClass, parentClass } from './index.styles.css';
          const Demo = () => (
              <div className={parentClass}>
                  <div className={childClass}>DEMO1</div>
                  <div className={childClass}>DEMO2</div>
                  <div className={childClass}>DEMO3</div>
              </div>
          );
          
          export default Demo;

          Atomic CSS

          原子 CSS 就像是實用工具優先(utility-first)CSS 的一個極端版本: 所有 CSS 類都有一個唯一的 CSS 規則。原子 CSS 最初是由 Thierry Koblentz (Yahoo!)在 2013 年挑戰 CSS 最佳實踐時使用的。在前端構建工具沒有成熟的時候,這種思想基本很難實現,但是現在借助 PostCSS 的力量,有一些原子化 CSS 庫例如 Tailwind CSS 開始被廣泛使用。

          .m-0 {
              margin: 0;
          }
          
          .text-red {  
              color: red;
          }

          下面這個在組件數量膨脹的情況下,使用原子化和不使用原子化的性能圖。可以看到不使用原子化 CSS 的時候是線性增長,而使用之后是對數增長。因為原子化 CSS 的上限是固定的,而普通 class 的上限是不確定的,隨著組件數量的增加,使用的原子化 CSS class 數量趨于穩定,而普通 class 數量依舊在一路狂奔。


          Tailwind CSS 這里就不多介紹了,其流行程度完全超過了流行程度最高的組件庫 MUI(人們都喜歡自己造輪子)。而 Tailwind CSS 的最新競對 UnoCSS,由 Vite 核心團隊打造,不使用 PostCSS,性能比 Tailwind CSS 快 100 倍。具體快的來龍去脈可以參考這篇文章:重新構想原子化 CSS。

          當然原子化 CSS 也有缺點:

          • 不易維護。如果要修改常用的原子類,如 m20(表示 margin 20px ),要么改變它的定義(違背它的命名),要么批量替換它的引用(費時費力)。如果只要修改部分引用,就更麻煩了。
          • 學習成本。影響開發效率,解決方案安裝對應的vscode插件,語法提示能夠幫助我們,但是仍然無法完全避免去翻官方文檔。

          總結

          希望你能通過以上,了解 CSS 的發展脈絡,在開發過程選擇合適的 CSS 技術提高我們的開發效率。隨著前端的發展,CSS 技術仍然在不斷進化,我們也要時刻保持學習的心態,去接觸最前沿的知識。

          數字化辦公的海洋里,PDF文檔如同一座座島嶼,而我們的目標則是將這些島嶼轉化為能在互聯網上自由航行的船只——HTML網頁文件。那么,有沒有一把神奇的鑰匙能夠輕松打開這扇轉換之門呢?當然有!那就是我們的“首助編輯高手”軟件。它不僅是一個好用的PDF編輯器,更是一個PDF轉換大師,能夠一鍵將PDF文檔批量轉換為HTML網頁文件,并且還能將新文件保存在原文件相同的位置,讓你的辦公之旅更加順暢無阻!

          1.軟件的PDF編輯工具不僅具備傳統的PDF新建和修改功能,更在格式轉換方面表現出色。

          2.用戶只需在軟件的批量轉換格式中選擇PDF轉HTML模式,即可輕松實現PDF到HTML的轉換。這一功能極大地提高了工作效率,讓用戶在處理大量PDF文檔時事半功倍。

          3.在實際操作中,我體驗到了軟件的便捷與高效。通過簡單的添加文件或添加文件夾中的文件操作,我成功導入了多個PDF文檔。

          4.在設置新文件保存位置時,我選擇了與原文件相同的位置,這樣便于后續管理和查找。

          5.點擊開始轉換按鈕后,軟件迅速完成了轉換工作,生成了對應的HTML網頁文件。

          6.查看轉換后的HTML文件時,我發現文檔的格式和排版都得到了很好的保留。

          7.與未轉換前的PDF文檔相比,HTML文件在網頁上的展示效果更加友好,同時也方便進行進一步的編輯和修改。

          首助編輯高手軟件就像是一位掌握了魔法技能的辦公助手,無論我們面對的是一座座PDF文檔的孤島,還是一片茫茫的HTML海洋,它都能輕松駕馭,幫助我們實現文檔的完美轉換。它的批量轉換功能不僅提高了我們的工作效率,更讓我們的辦公體驗變得更加輕松愉快。如果你還在為PDF和HTML之間的轉換而煩惱,那么不妨試試軟件吧!相信它一定會成為你辦公桌上不可或缺的好幫手。



          將網頁轉換為圖片,您可以使用一些庫和工具來實現。在前端開發中,常用的庫包括html2canvas和dom-to-image。這些庫允許您將HTML元素轉換為圖像。

          下面是使用html2canvas庫將網頁轉換為圖像的示例代碼:

          <!DOCTYPE html>  
          <html>  
          <head>  
            <title>Convert Webpage to Image</title>  
            <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.3.2/html2canvas.min.js"></script>  
          </head>  
          <body>  
            <div id="capture">  
              <!-- 在這里放置您想要轉換為圖像的HTML內容 -->  
              <h1>Hello, World!</h1>  
              <p>This is an example of converting a webpage to an image.</p>  
            </div>  
            
            <button onclick="convertToImage()">Convert to Image</button>  
            
            <script>  
              function convertToImage() {  
                html2canvas(document.getElementById("capture")).then(canvas => {  
                  var img = canvas.toDataURL("image/png");  
                  var link = document.createElement("a");  
                  link.href = img;  
                  link.download = "webpage.png";  
                  link.click();  
                });  
              }  
            </script>  
          </body>  
          </html>

          上述代碼使用html2canvas庫來將id為"capture"的div元素轉換為圖像。當用戶點擊"Convert to Image"按鈕時,convertToImage()函數會被調用。該函數使用html2canvas對指定的HTML元素進行截圖,并將結果轉換為一個包含圖像數據的URL。然后,創建一個隱藏的鏈接元素,將圖像數據URL設置為鏈接的href屬性,并模擬點擊該鏈接以下載圖像。最后,圖像將以PNG格式下載到用戶的設備上。

          請注意,html2canvas庫有一些限制和局限性,例如跨域圖像的限制、CSS樣式的復雜性等。確保在使用這些庫時仔細測試和驗證您的代碼,并查閱相關文檔以了解更多細節和選項。


          主站蜘蛛池模板: 一区二区三区日本电影| 91香蕉福利一区二区三区| 日本免费一区二区三区| 麻豆AV一区二区三区| 亚洲制服丝袜一区二区三区| 麻豆一区二区免费播放网站| 最新中文字幕一区二区乱码 | 区三区激情福利综合中文字幕在线一区亚洲视频1 | 国产产一区二区三区久久毛片国语| 手机福利视频一区二区| 国产一区二区三区在线电影| 国产日韩高清一区二区三区| 亚洲视频一区调教| 国产视频一区二区在线观看| 久99精品视频在线观看婷亚洲片国产一区一级在线 | 呦系列视频一区二区三区| 性色av闺蜜一区二区三区| 在线精品一区二区三区电影| 久久亚洲中文字幕精品一区| 台湾无码一区二区| 视频在线一区二区| 无码日韩精品一区二区免费暖暖| 成人精品一区二区不卡视频| 久久无码精品一区二区三区| 久久久久人妻精品一区二区三区| 国产精品久久亚洲一区二区| av无码人妻一区二区三区牛牛| 国产精品高清视亚洲一区二区| 一区二区三区在线观看免费| 99精品国产高清一区二区| 夜色阁亚洲一区二区三区| 久久精品国产第一区二区三区| 国产精品一区二区三区高清在线| 日本大香伊一区二区三区| 中文字幕日韩欧美一区二区三区| 日本成人一区二区| 日本免费精品一区二区三区| 国产精品福利一区二区| 真实国产乱子伦精品一区二区三区 | 欧美日韩精品一区二区在线视频 | 97精品国产福利一区二区三区|