PostCSS 架構
PostCSS 架構的概觀。對於想要為核心做出貢獻或更深入了解此工具的人來說,這會很有用。
概觀
本節說明 PostCSS 背後的想法
在深入了解 PostCSS 的開發之前,讓我們簡要說明 PostCSS 是什麼,什麼不是。
PostCSS
-
不是像
Sass
或Less
那樣的樣式預處理器。它沒有定義自訂語法和語意,它實際上不是一種語言。PostCSS 使用 CSS,可以輕鬆地與上述工具整合。話雖如此,任何有效的 CSS 都可以由 PostCSS 處理。
-
是一種 CSS 語法轉換工具
它允許您定義自訂 CSS 語法,外掛程式可以理解並轉換。話雖如此,PostCSS 不僅限於 CSS 規範,還包括 CSS 的語法定義方式。這樣一來,您可以定義自訂語法結構,例如 at-rule,這對於建立在 PostCSS 周圍的工具非常有幫助。PostCSS 扮演一個架構的角色,用於建置出色的 CSS 處理工具。
-
是 CSS 生態系統中的重要角色
許多好用的工具,例如
Autoprefixer
、Stylelint
、CSSnano
,都是建立在 PostCSS 生態系統上。您很有可能已經在不知不覺中使用它,只要查看您的node_modules
😀
工作流程
這是 PostCSS 整個工作流程的高階概觀

從上方的圖表中可以看到,PostCSS 架構相當簡單,但其中某些部分可能會被誤解。
你可以看到一個稱為剖析器的部分,此結構會在稍後詳細說明,現在只要把它想成一個可以理解你的 CSS 語法並建立其物件表示的結構即可。
話說回來,撰寫剖析器的方法有幾種。
-
撰寫一個包含字串轉換為 AST 的單一檔案
此方法相當普遍,例如,Rework 分析器就是以這種風格撰寫。但對於大型程式碼庫,程式碼會變得難以閱讀且相當緩慢。
-
將其拆分為詞彙分析/剖析步驟(原始字串 → 符號 → AST)
這是我們在 PostCSS 中執行的做法,也是最普遍的做法。許多剖析器,例如
@babel/parser
(Babel 背後的剖析器)、CSSTree
,都是以這種方式撰寫。將符號化與剖析步驟分開的主要原因是效能和抽象複雜性。
讓我們想想為什麼第二種方式更適合我們的需求。
首先,因為字串轉換為符號的步驟比剖析步驟花費更多時間。我們在大型原始字串上執行操作並逐字元處理它,這就是為什麼在效能方面這是一個非常低效率的操作,我們應該只執行一次。
但從另一方面來看,符號轉換為 AST 在邏輯上更為複雜,因此透過這種分離,我們可以撰寫非常快速的符號化程式(但有時會產生難以閱讀的程式碼)和易於閱讀(但緩慢)的剖析器。
總而言之,拆分為兩個步驟可以提升效能和程式碼可讀性。
現在讓我們更仔細地檢視在 PostCSS 工作流程中扮演主要角色的結構。
核心結構
-
分詞器
lib/tokenize.js
分詞器(又稱詞法分析器)在語法分析中扮演重要角色。
它接受 CSS 字串並傳回代幣清單。
代幣是一種簡單的結構,用於描述語法中的某些部分,例如
at-rule
、comment
或word
。它也可以包含位置資訊,以提供更具描述性的錯誤訊息。例如,如果我們考慮以下 CSS
.className { color: #FFF; }
PostCSS 中對應的代幣將會是
[ ["word", ".className", 1, 1, 1, 10] ["space", " "] ["{", "{", 1, 12] ["space", " "] ["word", "color", 1, 14, 1, 18] [":", ":", 1, 19] ["space", " "] ["word", "#FFF" , 1, 21, 1, 23] [";", ";", 1, 24] ["space", " "] ["}", "}", 1, 26] ]
從上面的範例中可以看到,單一代幣表示為清單,而且
space
代幣沒有位置資訊。讓我們更仔細地檢視單一代幣,例如
word
。正如所說的,每個代幣都表示為清單,並遵循此模式。const token = [ // represents token type 'word', // represents matched word '.className', // This two numbers represent start position of token. // It is optional value as we saw in the example above, // tokens like `space` don't have such information. // Here the first number is line number and the second one is corresponding column. 1, 1, // Next two numbers also optional and represent end position for multichar tokens like this one. Numbers follow same rule as was described above 1, 10 ]
代幣化可以有多種模式,PostCSS 的座右銘是效能和簡潔。代幣化是一種複雜的運算操作,而且會花費大量的語法分析時間(約 90%),這就是為什麼 PostCSS 的分詞器看起來很簡陋,但它已針對速度進行最佳化。任何類別等高階建構都會大幅拖慢分詞器。
PostCSS 的分詞器使用某種串流/鏈結 API,您可以在其中公開
nextToken()
方法給剖析器。這樣一來,我們可以為剖析器提供一個乾淨的介面,並透過僅儲存少數代幣(而非整個代幣清單)來減少記憶體使用量。 -
剖析器
lib/parse.js
、lib/parser.js
剖析器是負責對輸入 CSS 進行 語法分析 的主要結構。剖析器會產生一個稱為 抽象語法樹 (AST) 的結構,之後可以由外掛程式進行轉換。
剖析器與分詞器共同運作,並針對代幣(而非原始字串)進行操作,因為這將會是一個非常低效的操作。
它主要使用分詞器提供的
nextToken
和back
方法來取得單一或多個代幣,然後建構稱為Node
的 AST 部分。PostCSS 可產生多種節點類型,但它們全部繼承自基礎節點 類別。
-
處理器
lib/processor.js
處理器是一種非常單純的結構,用於初始化外掛程式和執行語法轉換
它僅公開少數 API 方法。可以在 API 上找到它們的說明
-
字串化器
lib/stringify.js
、lib/stringifier.js
字串化器是一種基礎類別,用於將修改過的 AST 轉換為純 CSS 字串。字串化器從提供的節點開始遍歷 AST,並呼叫對應的方法來產生它的原始字串表示形式。
最重要的方法是
Stringifier.stringify
,它接受初始節點和分號指示器。您可以透過查看 stringifier.js 來進一步了解
API 參考
可以在 這裡 找到更具描述性的 API 文件