成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

攜程商旅在 Atomic Css 下的探索

開發 前端
未來Css-In-Js 的 Atomic Css 解決方案無論是在業務代碼還是基礎 Components 中一定會是一個不錯的方案。

作者簡介

19組清風,攜程資深前端開發工程師,負責商旅前端公共基礎平臺建設,關注NodeJs、研究效能領域。

一、引言

三年前 Facebook 開始思考在目前設計系統下面臨的問題,那時它們在前端項目、系統組件等部分使用的是 cssmodule 的樣式方案。

直至今日,Facebook 已經將所有的 Web 前端使用 React 進行重寫的同時,也使用了一種新的 Atomic Css-in-JS 對于它們的 Css 方案進行了重寫。

最近,Facebook 團隊開源了他們內部的 Atomic Css 解決方案:stylex,正是這套解決方案讓 Facebook 首頁樣式文件體積減少了至少 80%。

這篇文章中我們就著 Atomic Css 來聊聊 Facebook 最近剛好開源的 stylex。

二、Atomic Css

2.1 概念

Atomic Css 是一種通過為每個樣式聲明創建一個規則來減少定義規則總量的方法。

舉一個不是那么恰當的例子,比如說你可以將 Atomic 理解為 a、b、c... 一個一個原子化的字母,而每一個元素最終生效的樣式則是通過 a、b、c... 這樣一個一個原子化的字母拼接而來。

假設我們想要得到一個長寬為百分百、背景色為紅色的正方形,那么使用 Atomic Css 的方式來表示的話:

.w-full { width: 100%; };
.h-full { height: 100% };
.bg-red { backgroundColor: red }
<div class="w-full h-full bg-red">Square</div>

2.2 權衡

在Acss首次提出 Atomic Css 的實現方案后,之后有關于 Atomic Css 的相關討論以及實踐在前端社區內就如雨后春筍般四處開花。

無論是 Acss、Unocss、Tailwind 等等之類 Css 庫,其實歸根結底都是來源于同一個實現思路:Atmoic Css,那么 Atomic Css 究竟有什么好處呢?

接下來,聊聊我們關于 Atomic Css 的看法。

2.2.1 多 Npm Package 下的樣式復雜性

我所在的團隊日常除了常規的前端頁面開發外,還負責了以下兩個方面:

  • 日常項目中和業務強相關的偏業務性組件。
  • 日常項目中和業務無關性質的基礎性組件。

無論是在業務還是基礎組件的開發和維護上,如何縮減相關組件體積以至于可以和使用到該組件的不同業務團隊最小化的結合一直是我們在尋求的目標。

舉一個比較簡單的例子,假設我們在開發一個 Button 組件的同時定義了一個 corp-button 的樣式:

.corp-button {
    height: 100%;
    width:  100%;
}

此時當我們再次開發另一個 input 組件時,大多時候我們其實會經常用到 height:100%; width:100% 的樣式,傳統的 Scoped Css 解決方案下難免我們會重新定義一份樣式聲明:

.corp-input {
    height: 100%;
    width: 100%;
    font-weight: 500;
}

顯而易見,往往在同一份組件庫代碼中不同的 class 定義存在無數重復的樣式聲明,無論是 CssModule 還是 Css-In-Js 都無法將這部分重復樣式聲明在構建/運行時刪除掉。

上邊的情況只是在單一存儲庫中很常見的問題,我們團隊日常負責的 NpmPackage 遠遠不止一個,所以 Atomic Css 的概念可以幫助解決這個問題。

只要可以保證 Atomic 原子性,那么無論是存在多少 Package ,也可以在項目中最大程度的保證這份樣式文件的復用。

舉一個簡單的例子,比如上述的 corp-button 使用 atomic css 的方案,可以拆分為更加原子化的 class 聲明:

.w-full {
    width: 100%;
};
.h-full {
    height: 100%;
}


.corp-button => Compiled => .w-full .h-full

而在 input 組件中同樣可以使用拆分后的 atomic:

.font-normal {
    font-weight: 500;
}


.corp-input => Compiled => .w-full .h-full .font-normal

這種情況下,Atomic Css 可以大大減少調用方在使用不同 Npm Package 下的樣式文件體積,從而對于頁面加載性能來說是一種極大的提升。

2.2.2 單一項目復雜度上升時的樣式文件體積

往往大多數前端團隊由于歷史、規模原因,無法左右依賴組件方的技術架構方案。

與其息息相關更多的是隨著頻繁、快速的業務迭代,帶來樣式文件復雜度直線上升的問題。

那么怎么解釋這里的樣式文件復雜度直接上升的問題呢,我們來看一個稍微抽象的例子。

比如 A 同學在負責 ProjectA 項目,跟隨著頻繁的業務迭代難免一直會有新的頁面、功能增加到現有的項目中。

那么,對于前端工程師來說,隨著需求的頻繁增加,難免需要增加很多個新的 class 來編寫這部分新增的樣式。

傳統的 CssModule 以及 Css-In-Js 方案,可以讓我們在 class 的聲明上無需考慮新命名和舊命名重復的問題,但它仍無法解決隨著新的需求到來,仍然會增加新的樣式聲明內容,從而帶來更大的樣式文件體積影響頁面性能。

如果我們使用 Atomic 方案來處理 Css 文件的話,無論在多么頻繁的需求迭代背景下,樣式文件體積并不會跟隨項目復雜度而直線上升,原子化的 Css 文件體積到達一個極限的拐點之后會漸漸趨于平穩。

上面的描述稍微有些抽象,但是并不難理解。就好比如果在項目中需要增加一個背景色為紅色,寬高均為百分五十的 div 時,在之前的方案中我們會直接聲明:

.new-demand-block {
    width: 50%;
    height: 50%;
    background: red;
}

最終構建之后的樣式文件中,會加入 .new-demand-block 這部分的樣式。

而如果使用 Aotmic Css 的方案,由于之前已經定義過 width:50%、height:50%、background:red 的 Atomic Class ,所以新的樣式文件中并不會存在這些根據新需求而來的樣式。

不過有些同學會疑惑,這不是會將樣式文件體積轉化到了 HTML 中去了嗎?

實際的確是這樣,但是這也僅僅是首屏 HTML 會攜帶這部分 Atomic ClassName。同時對于 HTML 模版中的相同 Atomic Css,Gzip 會幫我們把這部分重復的 ClassName 壓縮到一個足夠小的體積。

2.2.3 日常業務交付標準下的樣式復用“錯亂”性

同樣,Atomic Css 方案還有一個和日常開發息息相關的影響點。

相信絕大多數開發同學都會碰到,伴隨著新需求上線或者修復某些 Bug 的同時,突然發現影響了之前已經經過驗證的頁面樣式。

這也是 Utility Css 會帶來的問題,為了節省樣式體積或是節約開發成本,我們往往會選擇在項目中復用相同類型的樣式。

但是隨著項目日積月累,我們會面臨修改這部分樣式時帶來的隱患:修改 A 模塊的 class 樣式內容,或許會影響到 B、C 等模塊。

這無異對于開發還是測試來說都是一種災難,Atomic Css 的出現可以很好地幫助我們解決這個問題。

每一處的元素都是由一個一個 Atomic 組成的樣式,在編寫新的 Css 聲明時由于已經是 Atomic 方案,所以大可不必擔心樣式體積的冗余而抽離一些 Utility Css。

自然,當我們修改某一處樣式文件內容時,也完全無需擔心會影響到別的地方,因為每次我們修改的并不是 class 代表的意義,而是使用一個一個 Atomic Class 來拼裝獲得當前元素最終的樣式。

當前,如果你能保證你團隊的樣式系統是百分百的標準,以及 Utility 的聲明非常規范化,Atomic Css 在這個問題下的解決方案就稍微顯得有些牽強。不過在我看來,絕大多數業務項目由于客觀原因,是無法和組件庫之類的對齊做到百分百的樣式系統規范化。

2.3 成果 

樣式文件體積過大,?直是攜程商旅在性能上存在的痛點,我們也在積極探索通過 Atomic 的方式尋找更好的用戶體驗。目前我們在國際站 Trip.Biz 已經從 CssModule 的方案全量切入 Atomic 方案,在樣式文件體積上取得了指數級變化的成果。 

比如同樣為 App 端首頁,在采用 Atomic 方案后的國際站首頁對比 CssModule 方案的國內站首頁,相似的頁面樣式下,國際站首頁在首屏渲染時僅需要加載 13.2KB 樣式文件,而國內 App 端首頁則需要加載 694KB 樣式文件, 前后對比首屏需要加載的樣式文件體積足足相差 96% 。 

在國內站的改版頁面中,同樣也取得了顯著的成果。比如在商旅 PC新版大首頁中,前后同樣一個查詢框業務組件,在使用了 Atomic 方案之后,新版首頁中查詢框組件所需要的樣式規則完全可以被項目覆蓋,最終單個查詢框組件跟隨頁面編譯后的樣式文件體積可以趨近于0。

三、Stylex

3.1 開始之前

Atmoic Css 在 stylex 出現之前也有許多優秀的解決方案,比如 Tailwind、WindCss、UnoCss 等。

我們團隊目前在使用的也并非 stylex 而是 Tailwind ,這篇文章更多是和大家介紹 Stylex 的用法以及我個人對于 stylex 的一些見解。

我們完全不用片面的認為 Atomic Css 就一定是 Tailwind 或者 Stylex 之類的某種實現框架。

無論 tailwind 還是 stylex ,他們都是 Atomic Css 方案的不同實現方案而已,至于應該選擇哪一種框架來實現 Atomic Css ,更多還是根據大家各自團隊中的實際情況來見仁見智。

究竟是 Utility 方式的 Tailwind 還是 Css-In-Js 方式的 Stylex ,哪一種更優秀,這篇文章中并不會討論。討論這些,就好比我在告訴你應該使用 Vue 還是 React 來寫前端一樣。

3.2 簡介

stylex是 Facebook 最近開源的一套 Css-In-Js 的 Atomic Css 解決方案。

Stylex 的工作原理是通過 Babel 在編譯階段將編寫的 Css-In-JS 代碼生成一個一個 Atomic Css 樣式,為輸出的元素增加這些 classname 的同時最終輸出在樣式文件中。雖然寫法上和 Css-In-Js 類似,但是 stylex 幾乎沒有任何運行時的成本。

同時對于需要結合不同變量增加不同樣式的運行時場景,Stylex 會在必要時根據不同條件來快速的生成組件的類名字符串,添加到對應元素中。

3.3 stylex.create/stylex.props

我們可以通過 stylex.create 方法創建 Atomic 樣式內容,從而使用 stylex.props 將 stylex.create 方法生成的 Atomic Css 應用到元素上。

比如:

import * as stylex from '@stylexjs/stylex';


// stylex.create 創建樣式內容
const styles = stylex.create({
  root: {
    backgroundColor: 'red',
    padding: '1rem',
    paddingInlineStart: '2rem'
  },
  title: {
    backgroundColor: 'blue'
  },
  dynamic: (opacity) => ({
    opacity
  })
});


function HomePage() {
  return (
    // stylex.props 應用創建的樣式內容到元素上
    <div {...stylex.props(styles.root)}>
      <h2 {...stylex.props(styles.title)}>Stylex</h2>
      <p {...stylex.props(styles.dynamic(0.2))}>
        Introduction to the basics of stylex.
      </p>
    </div>
  );
}


export default HomePage;

上邊的代碼經過編譯后的 Css 樣式文件輸出如下:

.x1uz70x1:not(#\#){padding:1rem}
.x1t391ir:not(#\#):not(#\#){background-color:blue}
.xrkmrrc:not(#\#):not(#\#){background-color:red}
.x1u4uod0:not(#\#):not(#\#){opacity:var(--opacity,revert)}
.xld8u84:not(#\#):not(#\#){padding-inline-start:2rem}

我們可以看到對于 stylex.create 創建的樣式內容,均被編譯成為了一個一個 Atomic Css 的 classname。

同時對于頁面上的元素,在經過 stylex 的 babel 插件編譯后,元素的 classname 上會增加上一個又一個編譯后的 Atomic classname:

圖片

唯一需要注意的一點是:在 p 標簽中我們使用了 styles.dynamic,它表示一個動態生成的 Css 透明度樣式。

透過上述編譯后的內容,我們可以清楚地看到在 stylex 內部是將這部分需要運行時生成的 Css 樣式內容的值,編譯為了 Css 變量的形式。

從而對于需要使用到動態 Css 變量的元素,動態替換它的 Css 變量值從而實現更新元素樣式的效果,這個實現思路還是比較巧妙的。

3.4 stylex.defineVars/stylex.createTheme

3.4.1 stylex.defineVars

stylex 中還提供了 defineVars Api 來幫助我們快速定義樣式變量的值。

// src/components/ButtonTokens.stylex.ts
import * as stylex from '@stylexjs/stylex';


// 通過 stylex 定義一系列 Button 相關樣式變量
export const buttonTokens = stylex.defineVars({
  bgColor: 'green',
  textColor: 'red',
  cornerRadius: '4px',
  paddingBlock: '4px',
  paddingInline: '8px'
});


// src/components/Button.ts
import * as stylex from '@stylexjs/stylex';
import './ButtonTokens.stylex';
import { buttonTokens } from './ButtonTokens.stylex';


const styles = stylex.create({
  base: {
    borderWidth: 0,
    backgroundColor: buttonTokens.bgColor,
    color: buttonTokens.textColor,
    borderRadius: buttonTokens.cornerRadius,
    paddingBlock: buttonTokens.paddingBlock,
    paddingInline: buttonTokens.paddingInline
  }
});
function Button() {
  return <button {...stylex.props(styles.base)}>This is Single Button</button>;
}


export default Button;

需要額外注意的是,官網文檔中明確標注關于 defineVars 方法需要滿足在文件名為 .stylex.js/*.stylex.ts 的文件中被具名導出。

需要注意雖然文檔上沒提,但是 import './ButtonTokens.stylex'; 必不可少。如果缺少這句導入,實際樣式內容并不會正常顯示。

此時頁面中的 Button :

圖片

圖片

3.4.2 stylex.createTheme

stylex.createTheme 接受兩個參數,第一個參數為通過 defineVars 創建的變量集合,第二個參數為用于覆蓋第一個參數的值,它是一個對象。

我們可以通過 stylex.createTheme 創建一個 StyleXStyles 對象,從而提供給 stylesx.props 方法使用。

同時,我們也可以使用stylex.createTheme來通過覆蓋stylex.defineVars 聲明的變量,從而創建主題,比如:

我們對上述的按鈕稍作修改,讓按鈕可以支持一個自定義主題的傳入:

import * as stylex from '@stylexjs/stylex';
import './ButtonTokens.stylex';
import { buttonTokens } from './ButtonTokens.stylex';


const styles = stylex.create({
  base: {
    borderWidth: 0,
    backgroundColor: buttonTokens.bgColor,
    color: buttonTokens.textColor,
    borderRadius: buttonTokens.cornerRadius,
    paddingBlock: buttonTokens.paddingBlock,
    paddingInline: buttonTokens.paddingInline
  }
});


// Button 組件可以額外接受一個 theme 的主題
function Button(props: { theme?: stylex.Theme<typeof buttonTokens> }) {
  return (
    <button {...stylex.props(props.theme, styles.base)}>
      This is Single Button
    </button>
  );
}


export default Button;

然后再將使用 Button 的地方稍做修改:

import * as stylex from '@stylexjs/stylex';
import Button from './components/Button';
import { buttonTokens } from './components/ButtonTokens.stylex';


const otherTheme = stylex.createTheme(buttonTokens, {
  bgColor: '#000',
  textColor: 'yellow',
  cornerRadius: '4px',
  paddingBlock: '4px',
  paddingInline: '8px'
});


function HomePage() {
  return (
    <div>
      {/* 未傳入特定主題,使用默認主題 */}
      <Button />
      {/* 傳入特定主題,覆蓋原本主題 */}
      <Button theme={otherTheme} />
    </div>
  );
}


export default HomePage;

此時,頁面上會出現兩個不同主題的按鈕:

圖片

實際上 createTheme 對于默認的 defineVars 的覆蓋,也是通過 Css 變量優先級來確定主題優先級的:

圖片

圖片

紅色文字按鈕的樣式變量,來源于 defineVars 的全局 Css 變量,而黃色按鈕通過在元素上編譯為同樣的  Css 變量的方式,自然優先級會比全局 Css 更高。

四、展望

以上和大家簡單聊 stylex 的 Api,以及它的基本使用姿勢。

目前我們對于 stylex 也并沒有太多的實踐經驗,看起來相較于目前流行的 Tailwind 這種類似 Utility Css 的 atomic css 方案, Css-In-Js 的解決方案在代碼組織上,以及類型約束上,的確對于代碼的可讀性以及組織性會更加便攜一些。

不過 stylex 現階段無論是從構建生態、內置實現(比如#197,#40 都是我在編寫 Demo 時碰到的一些問題)來說,可能對于在生產應用上使用還是有所欠缺。

總的來講,未來Css-In-Js 的 Atomic Css 解決方案無論是在業務代碼還是基礎 Components 中一定會是一個不錯的方案。

后續我們也會關注 stylex 的更新,并帶來更多關于 stylex 的實踐,希望本文的內容可以幫助到大家。

責任編輯:張燕妮 來源: 攜程技術
相關推薦

2024-12-26 09:27:51

2024-12-18 10:03:30

2017-02-23 21:17:00

致遠

2023-06-06 11:49:24

2024-11-05 09:56:30

2023-10-27 09:34:34

攜程應用

2022-06-17 10:44:49

實體鏈接系統旅游AI知識圖譜攜程

2023-08-18 10:49:14

開發攜程

2022-08-06 08:23:47

云計算公有云廠商成本

2023-07-07 12:26:39

攜程開發

2014-12-25 17:51:07

2024-04-18 09:41:53

2024-03-22 15:09:32

2023-11-13 11:27:58

攜程可視化

2022-07-21 19:36:35

樂高攜程前端

2023-06-06 16:01:00

Web優化

2017-07-06 19:57:11

AndroidMVP攜程酒店

2022-11-29 20:32:07

2022-04-07 17:30:31

Flutter攜程火車票渲染
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 五月婷婷丁香 | 成人自拍视频网站 | 午夜在线影院 | 一区二区成人 | 在线观看视频一区二区三区 | 国产成人小视频 | 日韩久久精品 | 在线免费观看a级片 | 91精品国产91久久久久久最新 | 在线成人av | 久久宗合色 | 亚洲一区二区视频 | 人人叉| 中文字幕国产精品 | 在线亚洲人成电影网站色www | 激情黄色在线观看 | 美日韩中文字幕 | 亚洲精品一区二区三区蜜桃久 | 夜夜夜操 | 国产精品视频在线观看 | 黄色小视频入口 | 免费视频一区二区三区在线观看 | 在线看亚洲 | 亚洲综合色丁香婷婷六月图片 | 国产精品久久久久久久岛一牛影视 | 四季久久免费一区二区三区四区 | 麻豆一区二区三区精品视频 | 91久久久久 | 欧美一区二区三区在线看 | 一区精品在线观看 | 欧美日韩精品一区二区三区四区 | 日韩伦理一区二区 | 国产在线一区二 | 91原创视频在线观看 | 亚洲精品一区二区三区蜜桃久 | 亚洲国产欧美日韩 | 欧美激情精品久久久久 | 久久国产亚洲 | 国产一区二区三区四区五区加勒比 | 日操操夜操操 | 欧美亚洲成人网 |