【譯】統同樣式語言

統同樣式語言

在過去幾年中,咱們見證了 CSS-in-JS 的興起,尤爲是在 React 社區。但它也飽含爭議,不少人,尤爲是那些已經精通 CSS 的人,對此持懷疑態度。css

"爲何有人要在 JS 中寫 CSS?html

這簡直是一個可怕的想法!前端

希望他們學過 CSS !"react

若是這是你聽到 CSS-in-JS 時的反應,那麼請閱讀下去。咱們來看看爲何在 JavaScript 中編寫樣式並非一個可怕的想法,以及爲何我認爲你應該長期關注這個快速發展的領域。jquery

相互誤解的社區

React 社區常常被 CSS 社區誤解,反之亦然。對我來講這頗有趣,由於我同時混跡於這兩個社區。android

我從九十年代後期開始學習 HTML,而且從基於表格佈局的黑暗時代就開始專職於 CSS。受 CSS 禪意花園啓發,我是最先一批將現有代碼向語義化標籤和層疊樣式表遷移的開發者。不久後我開始癡迷於 HTML 和 JavaScript 的分離工做,在服務器渲染出來的頁面中使用非侵入式 JavaScript 同客戶端交互。圍繞這些實踐,咱們組成了一個很是小可是充滿活力的社區,而且咱們成爲了第一代前端開發人員,努力去解決各個瀏覽器的兼容性問題。webpack

在這種關注於web的背景下,你可能會認爲我會強烈反對 React 的 HTML-in-JS 模式,它彷佛違背了咱們所堅持的原則,但實際上偏偏相反。根據個人經驗,React 的組件化模型結合其服務端渲染的能力,終於爲咱們提供了一種構建大規模複雜單頁應用的方式,而且仍然能將快速、易訪問、漸進加強的應用推送給咱們的用戶。在咱們的旗艦產品 SEEK 上咱們就是這麼作的,它是一個 React 單頁應用,當 JavaScript 被禁用時,其核心搜索流程依然可用,由於咱們經過在服務器端運行同構的 JavaScript 代碼來實現優雅降級。ios

因此,請考慮將這篇文章做爲兩個社區之間相互示好的橄欖枝。讓咱們一塊兒努力理解 CSS-in-JS 此次轉變的實質所在。也許它不完美,也許你沒有計劃在你的產品中使用這門技術,也許它對你不是頗有說服力,可是至少值得你嘗試思考一下。git

爲何要使用 CSS-in-JS?

若是你熟悉我最近作的與 React 以及 CSS Modules相關的工做,你會驚訝地發現我是捍衛 CSS-in-JS 的。github

畢竟,一般那些但願樣式有局部做用域可是又不但願在 JS 中寫 CSS 的開發者纔會選擇使用 CSS Modules。事實上,我甚至在本身的工做中都不使用CSS-in-JS。

儘管如此,我仍然對 CSS-in-JS 社區保持濃厚的興趣,對他們不斷提出的創新保持密切關注。不只如此,我認爲這些應該一樣被更多的 CSS 社區所關注

緣由是什麼呢?

爲了更清楚地瞭解爲何人們選擇在 JavaScript 中編寫樣式,咱們將重點關注這種方式所帶來的實際性好處.

我把這些優勢分爲五個主要方面:

  1. 擁有做用域的樣式
  2. 抽取關鍵 CSS
  3. 更智能的優化
  4. 打包管理
  5. 在非瀏覽器環境下的樣式

讓咱們作進一步的瞭解,仔細看看 CSS-in-JS 在這幾個方面分別帶來了什麼。

1.

擁有做用域的樣式

衆所周知,想要在大規模項目中高效地構建 CSS 是很是困難的。當加入一個須要長期維護的項目時,咱們一般會發現 CSS 是系統中最複雜的部分。

爲了解決這個問題,CSS 社區已經投入了巨大的努力,經過採用 Nicole Sullivan 提出的 OOCSSJonathan Snook 提出的 SMACSS 均可以提升咱們樣式的可維護性。可是目前就流行程度而言,最佳的選擇毫無爭辯是 Yandex 提出的 BEM (Block Element Modifier)。

從根本上來講,BEM (純粹用於 CSS 時)只是一個命名規範,它要求樣式的類名要遵照 .Block__element--modifier 的模式。在任何使用 BEM 風格的代碼庫中,開發人員必須始終遵照 BEM 的規則。當被嚴格遵照時,BEM 的效果很好,可是爲何像做用域這種基礎的東西,卻只使用純粹的命名規範來限制呢?

不管是否有明確表示,大多數 CSS-in-JS 類庫的思路和 BEM 都很類似,它們努力將樣式獨立做用於單個 UI 組件,只不過他們用了徹底不一樣的實現方式。

那麼在實際代碼中是什麼樣子呢?當使用 Sunil Pai 開發的 glamor 時,代碼看起來像下面這樣:

import { css } from 'glamor'
const title = css({
  fontSize: '1.8em',
  fontFamily: 'Comic Sans MS',
  color: 'blue'
})
console.log(title)
// → 'css-1pyvz'複製代碼

你可能會注意到這段代碼中沒有 CSS 類。樣式再也不是對系統其餘地方定義的 class 的硬編碼引用,而是由咱們的工具庫自動生成的。咱們沒必要再擔憂選擇器會在全局做用域裏發生衝突,這也意味着咱們再也不須要替他們添加前綴了。

這個選擇器的做用域與上下文代碼的做用域一致。若是你但願在你應用的其餘部分使用這個規則,你就須要將它轉換成 JavaScript 模塊而且在須要使用的地方引用它。就保持代碼庫的可維護性而言,這是很是強大的,它確保了任何給定的樣式均可以像其餘代碼同樣容易追蹤來源

從僅僅靠命名約定來限制樣式的做用域到默認強制局部做用域樣式轉變,咱們已經提高了樣式的基本能力。BEM 的功能已經被默認使用了,而再也不是一個可選項。

在我繼續以前,我要澄清相當重要的一點。

它生成的是真正的 CSS,而不是內聯樣式

大多數早期的 CSS-in-JS 庫都是將樣式直接內聯到每一個元素上,可是這種模式有個嚴重的缺陷:'style' 屬性並不能勝任全部 CSS 的功能。大多數新的 CSS-in-JS 庫則側重於動態樣式表,在運行時從一個全局樣式集中插入和刪除規則。

舉個例子,讓咱們看看由 Oleg Slobodskoi 開發的 JSS,這是最先生成真正 CSS 的 CSS-in-JS 庫之一。

使用 JSS 時,你可使用標準的 CSS 特性,好比 hover 和媒體查詢,它們會映射成相應的 CSS 規則。

const styles = {
  button: {
    padding: '10px',
    '&:hover': {
      background: 'blue'
    }
  },
  '@media (min-width: 1024px)': {
    button: {
      padding: '20px'
    }
  }
}複製代碼

將這些樣式插入到文檔中後,你就可使用那些自動生成的類名。

const { classes } = jss.createStyleSheet(styles).attach()複製代碼

無論你是使用一個完整的框架,仍是簡單粗暴地使用 innerHTML,當用 JavaScript 生成 HTML 時,均可以使用這些生成的 代替硬編碼的類名。

document.body.innerHTML = `
  <h1 class="${classes.heading}">Hello World!</h1>
`複製代碼

可是單獨使用這種方式管理樣式並無帶來很大的優點,它一般須要和一些組件庫搭配使用。所以,能夠很容易找到適用於目前最流行庫的綁定方案。例如,JSS 能夠經過 react-jss 的幫助輕鬆地綁定到 React 組件上,在管理生命週期的同時,它能夠幫你給每一個組件插入一個小的樣式集。

import injectSheet from 'react-jss'
const Button = ({ classes, children }) => (
  <button className={classes.button}>
    <span className={classes.label}>
      {children}
    </span>
  </button>
)
export default injectSheet(styles)(Button)複製代碼

經過代碼層面上的緊密結合將咱們的樣式集中到組件上,咱們獲得了合乎 BEM 邏輯的結果。可是,CSS-in-JS 社區的許多人以爲提取、命名和複用組件的重要性在全部綁定樣式的樣板中都被遺棄了。

Glen MaddernMax Stoiber 提出了一個全新的思路來解決這個問題 —— styled-components

咱們強制性地直接建立組件,而不是建立樣式而後再手動地將他們綁定到組件上。

import styled from 'styled-components'

const Title = styled.h1`
  font-family: Comic Sans MS;
  color: blue;
`複製代碼

在應用這些樣式時,咱們不會將 class 添加到一個現有的元素上,而是簡單地渲染這些被生成的組件。

<Title>Hello World!</Title>複製代碼

styled-components 經過模板字面量的方式來使用傳統的 CSS 語法,但有人更喜歡使用數據結構。來自 PayPalKent C. Dodds 所提供的 Glamorous 是一個值得關注的替代方案。

Glamorous 提供了與 styled-components 相似的組件優先的 API,可是他用對象替代了字符串。這樣就無需在庫中引入一個 CSS 解析器,從而能夠下降庫的大小並提高性能。

import glamorous from 'glamorous'

const Title = glamorous.h1({
  fontFamily: 'Comic Sans MS',
  color: 'blue'
})複製代碼

不管你使用哪一種語法來描述你的樣式,他們都再也不僅僅做用於某個組件,而是成爲組件不可分割的一部分。當使用一個像 React 這樣的庫時,組件是基本的構建塊,而如今咱們的樣式也成了構建這個架構的核心部分。既然咱們能將應用程序中的全部內容都描述爲組件,那麼爲何樣式不行呢?

對於那些有豐富 BEM 開發經驗的工程師來講,咱們對系統改造所帶來的提高意義並非很大。然而事實上,CSS Modules 讓你在不用放棄所熟悉的 CSS 工具生態的同時得到了這些提高,這也是不少項目堅持使用 CSS Modules 的緣由,他們能夠在保持其常規 CSS 編碼習慣的同時充分解決編寫大規模 CSS 所遇到的問題。

然而,當咱們開始在這些基本概念之上進行構建時,事情開始變得更有趣。

2.

抽取關鍵 CSS

最近,在 document 的頭部內聯關鍵樣式已經成爲一種最佳實踐,只提供當前頁面所需的樣式從而下降了首屏渲染時間。這與咱們經常使用的樣式加載方式造成了鮮明對比,以前咱們一般會強制瀏覽器在渲染以前下載應用的全部樣式。

雖然像 Addy Osmani 提供的 critical 這類工具能夠用於提取和內聯關鍵 CSS,可是他們沒法從根本上改變關鍵 CSS 難以維護和自動化的事實。這只是一個可選擇用來作性能優化的奇技淫巧,因此大部分項目彷佛放棄了這一步。

CSS-in-JS 則徹底不一樣。

當你的應用使用服務端渲染時,提取關鍵 CSS 將不只僅是優化,而是服務器端 CSS-in-JS 的首要工做。

舉個例子,當使用 Khan Academy 開發的 Aphrodite 時,能夠經過它的 css 函數來跟蹤在此次渲染過程當中使用的樣式,而且將生成的 class 內聯到元素上。

import { StyleSheet, css } from 'aphrodite'
const styles = StyleSheet.create({
  title: { ... }
})
const Heading = ({ children }) => (
  <h1 className={css(styles.heading)}>{ children }</h1>
)複製代碼

即使你全部的樣式都是在 JavaScript 中定義的,你也能夠很輕鬆地提取當前頁面所須要的全部樣式並生成一個 CSS 字符串,在執行服務端渲染時將它們插入到 document 的頭部。

import { StyleSheetServer } from 'aphrodite';

const { html, css } = StyleSheetServer.renderStatic(() => {
  return ReactDOMServer.renderToString(<App/>);
});複製代碼

如今你能夠像這樣渲染你的關鍵 CSS 代碼塊:

const criticalCSS = `
  <style data-aphrodite>
    ${css.content}
  </style>
`;複製代碼

若是你研究過 React 的服務端渲染模型,你可能會發現這個模式很是眼熟。在 React 中,你的組件是在 JavaScript 中定義他們的標籤的,但卻能夠在服務器端渲染成常規的 HTML 字符串。

若是你使用漸進加強的方式構建你的應用,即使整個項目可能所有是用 JavaScript 寫的,客戶端也可能根本就不須要 JavaScript。

無論怎樣,對於客戶端運行的代碼而言,其打包後的 bundle 都要包含啓動單頁應用所須要的代碼。這些代碼可讓頁面瞬間活起來,瀏覽器中的渲染也是從這裏開始的。

因爲在服務器上渲染 HTML 和 CSS 是同時進行的,正如前面的例子所示,像 Aphrodite 這樣的庫一般會以一個函數調用的方式幫助咱們流式生成關鍵 CSS 和服務端渲染的 HTML。如今,咱們能夠用相似的方式將咱們的 React 組件渲染成靜態 HTML。

const appHtml = `
  <div id="root">
    ${html}
  </div>
`複製代碼

經過在服務器端使用 CSS-in-JS,咱們的單頁應用不只能夠脫離 JavaScript 工做,它甚至能夠渲染的更快

正若有做用域的 CSS 選擇器同樣,渲染關鍵 CSS 這個最佳實踐如今也是默認具有的能力了,而不是被選擇性使用的

3.

更智能的優化

咱們最近看到了構建 CSS 的新方式的興起,好比 YahooAtomic CSSAdam MorseTachyons,它們更推薦使用短小的、單一用途的 class,而不是語義化的 class。舉個例子,當使用 Atomic CSS 時,你將使用相似於函數調用的語法來添加類名,而且它們會被用來生成合適的樣式表。

<div class="Bgc(#0280ae.5) C(#fff) P(20px)">
  Atomic CSS
</div>複製代碼

這種作法的目的是經過最大化地提升 class 的複用性,以及有效地將 class 像內聯樣式同樣對待,lai確保打包出來的 CSS 儘量的精簡。雖然文件大小的減小很容易體現,但對於你的代碼庫和團隊成員的影響彷佛是微乎其微的。不過這些包含了對 CSS 和 HTML 更改的優化,因爲其自身性質,成就了一個更具意義的架構。

正如咱們以前介紹的那樣,當使用 CSS-in-JS 或者 CSS Modules 時,你再也不須要在 HTML 中硬編碼你的 class,而是動態引用由庫或者構建工具自動生成的 JavaScript 值。

咱們再也不這樣寫樣式:

<aside className="sidebar" />複製代碼

而是這樣:

<aside className={styles.sidebar} />複製代碼

這個變化表面上看起來也許沒什麼,可是從如何管理標記語言和樣式之間的關係上來講,這倒是一個里程碑式的改變。經過給予咱們的 CSS 工具修改樣式的能力,尤爲是修改最終應用到元素上的 class 的能力,咱們爲樣式表解鎖了一個全新的優化方式。

若是看看上面的例子,就會發現 "styles.sidebar" 對應了一個字符串,但並無限制它只能是一個單獨的 class。咱們都知道,它能夠很容易地成爲一個包含十幾個 class 的字符串。

<aside className={styles.sidebar} />
// Could easily resolve to this:
<aside className={'class1 class2 class3 class4'} />複製代碼

若是咱們能夠優化咱們的樣式,爲每一套樣式生成多個 class,咱們就能夠作一些真正有趣的事。

我最喜歡的例子是 Ryan Tsao 編寫的 Styletron

就像 CSS-in-JS 和 CSS Modules 自動添加 BEM 風格的前綴同樣,Styletron 對 Atomic CSS 作了一樣的事情。

它的核心 API 只專一於一件事 —— 爲每一個由屬性、值、媒體查詢組合起來的樣式定義一個單獨的 CSS 規則,而後返回一個自動生成的 class。

import styletron from 'styletron';
styletron.injectDeclaration({
  prop: 'color',
  val: 'red',
  media: '(min-width: 800px)'
});
// → 'a'複製代碼

固然,Styletron 也提供了一些高級 API,好比它的 injectStyle 函數容許一次定義多個規則。

import { injectStyle } from 'styletron-utils';
injectStyle(styletron, {
  color: 'red',
  display: 'inline-block'
});
// → 'a d'
injectStyle(styletron, {
  color: 'red',
  fontSize: '1.6em'
});
// → 'a e'複製代碼

請注意上面生成的兩組類名之間的相同點。

經過放棄對 class 自己的低級控制,而僅定義所須要的樣式,就可讓工具庫幫咱們生成最佳的原子 class 集合。

過去咱們只能經過手工查找的方式將樣式拆分紅可複用的 class,如今已經能夠徹底自動化的完成這種優化了。你應該也開始注意到這種趨勢了。原子 CSS 已是默認具有的能力,而再也不是被選擇性使用的

4.

打包管理

在深刻討論這一點以前,咱們先停下來思考一個看似簡單的問題。

咱們如何相互分享 CSS?

咱們已經從手動下載 CSS 文件轉變爲使用像 Bower 這種前端特定的包管理工具,如今則能夠經過 npm 使用 Browserifywebpack。雖然這些工具已經能夠自動引入外部依賴包裏的 CSS,可是目前前端社區大多仍是手動處理 CSS 的依賴關係。

不管使用哪一種方式,你得清楚一件事:CSS 之間的依賴並非很好處理。

正如許多人還記得的同樣,在使用 Bower 和 npm 管理 JavaScript 模塊時,出現過相似的狀況。

Bower 沒有指定任何特定的模塊格式,而發佈到 npm 的模塊則要求使用 CommonJS 模塊格式。這種不一致,對發佈到每一個平臺的包數量產生了巨大的影響。

規模小可是有嵌套依賴關係的模塊更願意使用 npm,Bower 則吸引了大型而又獨立的模塊,其中可能也就有兩三個模塊,再加幾個插件。因爲在 Bower 中你的依賴關係沒有一個模塊系統去做支撐,每一個包沒法輕鬆地利用它本身的依賴關係,因此在整合這一塊,基本上就留給開發者手動去操做了。

所以,隨着時間的推移,npm 上的模塊數量呈指數性增加,而 Bower 只能是有限的線性增加。雖然這多是各類緣由致使的,但很公平地說,主要仍是由每一個平臺是否容許模塊在運行時互相引用致使的。

不幸的是,對於 CSS 社區來講這太熟悉了,咱們發現相對於 npm 上的 JavaScript 包來講,獨立的 CSS 模塊的數量也增加的很慢。

若是咱們也想實現 npm 的指數增加呢?若是咱們想依賴不一樣大小不一樣層次的複雜模塊,而不是專一於大型、全面的框架呢?爲了作到這一點,咱們不只須要一個包管理器,還須要一個合適的模塊格式。

這是否意味着咱們須要專門爲 CSS 或者 Sass 和 Less 這樣的預處理器設計一個包管理工具?

真正有趣的是,咱們已經經過 HTML 進行了相似的實現。若是你就如何分享 HTML 問我相似的問題,你可能立刻就會意識到,咱們幾乎不會直接分享原始的 HTML —— 咱們分享 HTML-in-JS

咱們經過 jQuery 插件Angular 指令React 組件來實現這個功能。咱們的大組件是由一些獨立發佈在 npm 上,包含本身 HTML 的小組件組成的。原生 HTML 格式也許沒有這種能力,可是經過將 HTML 嵌入到完整的編程語言中,咱們就能夠很輕鬆的突破這個限制。

若是咱們像 HTML 那樣,經過 JavaScript 去分享以及生成 CSS 呢?能不能使用返回對象和字符串的函數而不是使用 mixins ?又或者咱們利用 Object.assign 和新的 object spread 操做符merge 對象而不是用 extending classes 呢?

const styles = {
  ...rules,
  ...moreRules,
  fontFamily: 'Comic Sans MS',
  color: 'blue'
}複製代碼

一旦咱們開始用這種方式編寫咱們的樣式,咱們就可使用相同的模式、相同的工具、相同的基礎架構、相同的生態系統來編寫和分享咱們的樣式代碼,就像咱們應用程序中的任何其餘代碼同樣。

Max StoiberNik GrafBrian Hough 提供的 Polished 就是一個你如何從中受益的良好示例。

Polished 就像是 CSS-in-JS 界的 Lodash,它提供了一整套完整的 mixins、顏色函數、一些速寫方法等等,使得那些使用 Sass 的開發者能夠熟練地在 JavaScript 中編寫樣式。如今有一個最大的區別就是這些代碼在複用、測試和分享方面,都提升了一個層級,而且可以完整的使用 JavaScript 模塊生態系統。

那麼,當談到 CSS 時,咱們如何得到和 npm 上其餘模塊類似的開源程度,以及如何用一些小的可複用的開源包組合成大型樣式集合?奇怪的是,咱們最終能夠經過將咱們的 CSS 嵌入另外一種語言而且徹底擁抱 JavaScript 模塊實現了這一點。

5.

在非瀏覽器環境下的樣式

到目前爲止,個人文章已經涵蓋了全部的要點,雖然在 JavaScript 中編寫 CSS 會更加便捷,可是常規的 CSS 也能夠實現這些功能。這也是我把最有趣、最面向將來的一點留到如今的緣由。也許它不必定能在現在的 CSS-in-JS 社區中發揮巨大的做用,但它可能會成爲設計領域將來發展的基石。它不只會影響開發人員,也會影響設計師,最終它將改變這兩個領域相互溝通的方式。

爲了引入它,我先簡單介紹一下 React。

React 的理念是用組件做爲最終渲染的中間層。在瀏覽器中工做時,咱們構建複雜的虛擬 DOM 樹而不是直接操做 DOM 元素。

有趣的是,DOM 渲染相關的代碼並不屬於 React 的核心部分,而是由 react-dom 提供的。

import { render } from 'react-dom'複製代碼

儘管最初 React 是爲 DOM 設計的,而且大部分狀況下仍是在瀏覽器中使用,可是這種模式也容許 React 經過簡單地引入新的渲染引擎就能從容面對各類不一樣的使用環境。

JSX 不只僅能夠用於虛擬 DOM,他能夠用在任何的虛擬視圖上。

這就是 React Native 的工做原理,咱們經過編寫那些渲染成 native 的組件以實現用 JavaScript 編寫真正的 native 應用,好比咱們用 ViewText 取代了 divspan

從 CSS 的角度來看,React Native 最有趣的就是它擁有本身特有的 StyleSheet API

var styles = StyleSheet.create({
  container: {
    borderRadius: 4,
    borderWidth: 0.5,
    borderColor: '#d6d7da',
  },
  title: {
    fontSize: 19,
    fontWeight: 'bold',
  },
  activeTitle: {
    color: 'red',
  }
})複製代碼

這裏你會看到一組熟悉的樣式,在這種狀況下能夠覆蓋顏色、字體和邊框樣式。

這些規則都很是簡單,而且很容易映射到大部分的 UI 環境上,可是當涉及到 native 佈局時,事情就變得很是有趣了。

var styles = StyleSheet.create({
  container: {
    display: 'flex'
  }
})複製代碼

儘管運行在瀏覽器環境以外,React Native 有本身的 flexbox 的 native 實現

最初發布時它是一個名爲 css-layout 的 JavaScript 模塊,徹底用 JavaScript 從新實現了 flexbox(包含充分的測試),爲了更好的可移植性它如今已經遷移到 C 語言。

鑑於這個項目的影響力和重要性,它被賦予了一個獨立的重要品牌 ——— Yoga

即便 Yoga 徹底是爲了把 CSS 概念移植到非瀏覽器環境而生,但經過僅僅專一 CSS 特性的子集,它已經統治了一些潛在的其餘領域。

"Yoga 的重點是成爲一個有表現力的佈局框架,而不是去實現一套完整的 CSS"

這看起來彷佛很難實現,可是當你回顧 CSS 體系的歷史時會發現使用 CSS 進行規模化的工做就是選擇一個合適的語言子集

在 Yoga 的例子裏,他們避免了層疊樣式,由於這樣有利於控制樣式的做用域,而且將佈局引擎徹底集中在 flexbox 上。雖然這樣會喪失不少功能,但它也爲那些須要嵌入樣式的跨平臺組件創造了驚人的機會,咱們已經發現幾個試圖利用這個特性的開源項目。

Nicolas Gallagher 開發的 React Native for Web 旨在成爲 react-native 的一個替代品。當使用 webpack 這類打包工具時,能夠利用 alias 輕鬆替換第三方庫。

module: {
  alias: {
    'react-native': 'react-native-web'
  }
}複製代碼

使用 React Native for Web 後能夠在瀏覽器環境中使用 React Native 組件,包括 React Native StyleSheet API 的瀏覽器部分。

一樣,Leland Richardson 開發的 react-primitives 也提供了一套跨平臺的基礎組件集合,它根據目標平臺來抽象具體的實現細節,爲跨平臺組件創造可行的標準。

甚至 微軟 也推出了 ReactXP,這個庫旨在簡化跨 web 和 native 的工做流,它也有本身的跨平臺樣式實現

即便你不爲 native 應用程序編寫代碼,也有很重要的一點要注意:擁有一個真正的跨平臺的組件抽象,可以幫咱們有針對性地應對各類各樣的環境,有時你都沒法預測會遇到哪些狀況。

我所見過的最使人震驚的例子是 AirbnbJon Gold 開發的 react-sketchapp

咱們中不少人都花費了大量時間去嘗試標準化咱們的設計語言,而且儘量的避免系統中的重複部分。不幸的是,儘管咱們但願樣式是惟一的,但咱們最少也會有兩個來源 —— 開發人員的動態樣式以及設計師的靜態樣式。雖然這已經比咱們以前的模式好了不少,可是它仍然須要咱們手工的將樣式從 Sketch 這樣的設計工具同步到代碼裏。這也是 react-sketchapp 被開發出來的緣由。

感謝 Sketch 的 JavaScript API,以及 React 與不一樣渲染引擎相連的能力,react-sketchapp 讓咱們能夠利用跨平臺的 React 組件並在 Sketch 文檔裏渲染他們。

沒必要多說,這極可能改變設計師和開發人員的合做方式。如今,當咱們對設計進行迭代時,不管在設計工具仍是開發者工具上,咱們均可以經過相同的聲明引用同一個組件。

經過 Sketch 中的 symbolsReact 中的組件,咱們的行業從本質上開始匯合成同一個抽象,而且經過分享相同的工具咱們能夠更緊密的協做。

這麼多新的嘗試都來自 React 和其周邊的社區,這並非巧合。

在組件架構中,優先級最高的就是將組件的關注點集中在一塊兒。這天然包括它的局部做用域樣式,也要感謝 RelayApollo 這兩個庫,他們讓咱們能夠往數據獲取這些更復雜的方向延伸。結果就是他們釋放了巨大的潛力,而咱們如今所瞭解的,只是其中冰山一角。

這對咱們的樣式編寫以及架構中的任何部分都產生了積極的影響。

經過將咱們開發組件的模式統一到單一語言上,咱們可以從功能上,而不是從技術上,將咱們的關注點進行更好的分離。好比咱們能夠將組件的全部內容都限制在本身的做用域內,從他們擴展成大型的可維護的系統,用以前沒法使用的方式進行優化,更便捷的分享咱們的工做,以及利用小型開源模塊構建大型應用程序。更重要的是,咱們依然遵循漸進加強的理念,也不會放棄那些被認爲是認真對待 web 平臺的理念。

最重要的是,我對使用單一語言編寫出的組件的潛力感到興奮,他們造成了一種新的、統一的樣式語言基礎,並以一種史無前例的方式統一了前端社區。

在 SEEK,咱們正在努力利用這一特性,咱們圍繞組件模型來構建在線樣式指南,其中語義化、交互和視覺風格都統一在一個單獨的抽象中。這造成了開發人員和設計師之間共享的通用設計語言。

構建一個頁面應該儘量的和拼裝組件同樣簡單,這樣能夠確保咱們的工做保持較高的質量,而且容許咱們在產品上線好久後也有能力去升級其設計語言。

import {
  PageBlock,
  Card,
  Text
} from 'seek-style-guide/react'
const App = () => (
  <PageBlock>
    <Card>
      <Text heading>Hello World!</Text>
    </Card>
  </PageBlock>
)複製代碼

儘管咱們的樣式指南是用 React、webpack 和 CSS Modules 構建的,但該架構剛好反映了在使用 CSS-in-JS 構建的任何系統中您都須要注意哪些。技術選型可能有不一樣,可是核心理念是同樣的。

然而,將來這些技術選型可能會以一種意想不到的方式進行轉變,所以關注這個領域對於咱們組件生態系統的持續發展相當重要。咱們如今可能不會用 CSS-in-JS 這項技術,可是極可能沒過多久就會出現一個使人信服的理由讓咱們使用它。

CSS-in-JS 在短期裏已經有了出人意料的發展,但更重要的是,它只是這個宏偉藍圖的開始。

它還有很大的改進空間,而且它的創新尚未中止的跡象。新的庫正不斷涌現,它們解決了將來會出現的問題而且提高了開發人員的體驗 —— 好比性能的提高、在構建時抽取靜態 CSS、支持 CSS 變量以及下降了前端開發人員的入門門檻。

這也是 CSS 社區的准入門檻。不管他們對咱們的工做流程有多大的改動,都不會改變你仍然須要學習 CSS 的事實

咱們可能使用不一樣的語法,也可能以不一樣的方式構建咱們的應用,可是 CSS 的基本構建塊不會改變。一樣,咱們行業向組件架構的轉變是不可避免的,經過這種方式從新構思前端開發的意願只會愈來愈強烈。咱們很是須要共同合做以確保咱們的解決方案能夠普遍適用於各類背景的開發人員,不管是專一於設計的,工程的或者對這兩方面都很關注的開發者。

雖然有時咱們的觀點不一致,可是 CSS 和 JS 社區對於改進前端,更加認真地對待 Web 平臺以及改進咱們下一代 web 開發流程都有很大的熱情。社區的潛力是巨大的,並且到目前爲止,儘管咱們已經解決了大量的問題,仍然有不少工做尚未完成。

到這裏,可能你依然沒有被說服,可是不要緊。雖然如今在工做上使用 CSS-in-JS 並非很合理,但我但願它有合適的緣由,而不是僅僅由於語法就反對它。

不管如何,將來幾年這種編寫樣式的方式可能會愈來愈流行,而且值得關注的是它發展的很是快。我衷心但願你能夠加入咱們,不管是經過貢獻代碼仍是簡單地參與咱們的對話討論,都能使下一代 CSS 工具儘量有效地服務於全部前端開發人員。或者,至少我但願我已經讓大家瞭解了爲何人們對這一塊如此飽含激情,或者,至少了解爲何這不是一個愚蠢的點子。

這篇文章是我在德國柏林參加 CSSconf EU 2017 作相同主題演講時撰寫的,而且如今能夠在 YouTube 上看到相關視頻。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃

相關文章
相關標籤/搜索