撰寫 PostCSS 外掛程式
連結
文件
支援
- 提出問題
- PostCSS twitter 提供最新更新。
步驟 1:建立一個概念
撰寫新的 PostCSS 外掛程式將有助於你在許多領域的工作
- 相容性修正:如果你總是忘記新增瀏覽器相容性駭客,你可以建立 PostCSS 外掛程式,為你自動插入這個駭客。
postcss-flexbugs-fixes
和postcss-100vh-fix
是很好的範例。 - 自動化例行作業:讓電腦執行例行作業,讓你自己有時間進行創意工作。例如,使用 RTLCSS 的 PostCSS 可以自動將設計轉換成從右到左的語言(例如阿拉伯語或希伯來語),或使用 postcss-dark-theme-class` 可以插入深色/淺色主題切換器的媒體查詢。
- 防止常見錯誤:「如果一個錯誤發生兩次,它就會再次發生。」PostCSS 外掛程式可以檢查你的原始碼是否有常見錯誤,並節省你進行不必要的除錯時間。這樣做的最佳方法是 撰寫新的 Stylelint 外掛程式(Stylelint 內部使用 PostCSS)。
- 增加程式碼可維護性: CSS 模組 或
postcss-autoreset
是 PostCSS 如何透過隔離增加程式碼可維護性的絕佳範例。 - Polyfills:我們已經在
postcss-preset-env
中為 CSS 草案提供了許多 polyfills。如果你發現新的草案,你可以新增一個新的外掛程式,並將其傳送至這個預設。 - 新的 CSS 語法:我們建議避免在 CSS 中新增新的語法。如果你想要新增一個新功能,最好寫一個 CSS 草案提案,將其傳送至 CSSWG,然後實作 polyfill。
postcss-easing-gradients
與 這個提案 就是一個很好的範例。然而,有許多情況下你無法傳送提案。例如,瀏覽器的剖析器效能大幅限制了 CSSWG 的巢狀語法,你可能想要從 `postcss-nested 取得非官方的 Sass 類似語法。
步驟 2:建立一個專案
撰寫外掛程式有兩種方式
- 建立一個私人外掛程式。僅當外掛程式與專案的特定事項相關時才使用這種方式。例如,你想要為你的獨特 UI 函式庫自動化一個特定任務。
- 發布一個公開外掛程式。這始終是建議的方式。請記住,即使在 Google 中,私人的前端系統也常常得不到維護。另一方面,許多熱門外掛程式都是在閉源專案中開發時建立的。
對於私人外掛程式
- 在
postcss/
資料夾中建立一個新檔案,並將其命名為你的外掛程式。 - 從我們的樣板程式碼複製 外掛程式樣板。
對於公開外掛程式
- 使用 PostCSS 外掛樣板 中的指南建立外掛目錄。
- 在 GitHub 或 GitLab 上建立儲存庫。
- 在那裡發布您的程式碼。
module.exports = (opts = {}) => {
// Plugin creator to check options or prepare caches
return {
postcssPlugin: 'PLUGIN NAME'
// Plugin listeners
}
}
module.exports.postcss = true
步驟 3:尋找節點
大多數的 PostCSS 外掛會做兩件事
- 在 CSS 中尋找某些東西(例如
will-change
屬性)。 - 變更找到的元素(例如在
will-change
之前插入transform: translateZ(0)
作為舊瀏覽器的多重載入)。
PostCSS 將 CSS 解析成節點樹(我們稱之為 AST)。此樹可能包含
Root
:樹頂端的節點,代表 CSS 檔案。AtRule
:以@
開頭的陳述,例如@charset "UTF-8"
或@media (screen) {}
。Rule
:內含宣告的選擇器。例如input, button {}
。Declaration
:鍵值對,例如color: black
;Comment
:獨立的註解。選擇器、at-rule 參數和值內的註解儲存在節點的raws
屬性中。
您可以使用 AST Explorer 瞭解 PostCSS 如何將不同的 CSS 轉換成 AST。
您可以透過將方法新增到外掛物件來尋找具有特定類型的所有節點
module.exports = (opts = {}) => {
return {
postcssPlugin: 'PLUGIN NAME',
Once (root) {
// Calls once per file, since every file has single Root
},
Declaration (decl) {
// All declaration nodes
}
}
}
module.exports.postcss = true
以下是 外掛事件 的完整清單。
如果您需要具有特定名稱的宣告或 at-rule,可以使用快速搜尋
Declaration: {
color: decl => {
// All `color` declarations
}
'*': decl => {
// All declarations
}
},
AtRule: {
media: atRule => {
// All @media at-rules
}
}
對於其他情況,您可以使用正規表示式或特定剖析器
- 選擇器剖析器
- 值剖析器
- 維度剖析器,適用於
number
、length
和percentage
。 - 媒體查詢剖析器
- 字型剖析器
- 邊界剖析器適用於
margin
、padding
和border
屬性。
其他用於分析 AST 的工具
別忘了正規表示式和剖析器是繁重的任務。在使用繁重的工具檢查節點之前,您可以使用 String#includes()
進行快速測試
if (decl.value.includes('gradient(')) {
let value = valueParser(decl.value)
…
}
有兩種類型的監聽器:進入和離開。Once
、Root
、AtRule
和 Rule
將在處理子節點之前被呼叫。OnceExit
、RootExit
、AtRuleExit
和 RuleExit
則在處理節點內的所有子節點之後被呼叫。
您可能希望在監聽器之間重複使用一些資料。您可以使用執行階段定義的監聽器來執行此操作
module.exports = (opts = {}) => {
return {
postcssPlugin: 'vars-collector',
prepare (result) {
const variables = {}
return {
Declaration (node) {
if (node.variable) {
variables[node.prop] = node.value
}
},
OnceExit () {
console.log(variables)
}
}
}
}
}
您可以使用 prepare()
動態產生監聽器。例如,使用 Browserslist 來取得宣告屬性。
步驟 4:變更節點
當您找到正確的節點時,您需要變更它們或在它們周圍插入/刪除其他節點。
PostCSS 節點具有類 DOM 的 API,可轉換 AST。查看我們的 API 文件。節點具有用於在周圍移動的方法(例如 Node#next
或 Node#parent
),查看子節點(例如 Container#some
),移除節點或在其中新增節點。
外掛程式的函式會在第二個引數中接收節點建立器
Declaration (node, { Rule }) {
let newRule = new Rule({ selector: 'a', source: node.source })
node.root().append(newRule)
newRule.append(node)
}
如果您新增了新節點,複製 Node#source
以產生正確的原始碼對應非常重要。
外掛程式會重新檢視您變更或新增的所有節點。如果您變更任何子節點,外掛程式也會重新檢視父節點。只有 Once
和 OnceExit
不會再次被呼叫。
const plugin = () => {
return {
postcssPlugin: 'to-red',
Rule (rule) {
console.log(rule.toString())
},
Declaration (decl) {
console.log(decl.toString())
decl.value = 'red'
}
}
}
plugin.postcss = true
await postcss([plugin]).process('a { color: black }', { from })
// => a { color: black }
// => color: black
// => a { color: red }
// => color: red
由於訪客會在任何變更時重新拜訪節點,僅新增子節點將導致無限迴圈。若要防止此情況,您需要檢查是否已處理此節點
Declaration: {
'will-change': decl => {
if (decl.parent.some(decl => decl.prop === 'transform')) {
decl.cloneBefore({ prop: 'transform', value: 'translate3d(0, 0, 0)' })
}
}
}
您也可以使用 Symbol
來標記已處理的節點
const processed = Symbol('processed')
const plugin = () => {
return {
postcssPlugin: 'example',
Rule (rule) {
if (!rule[processed]) {
process(rule)
rule[processed] = true
}
}
}
}
plugin.postcss = true
第二個引數也有 result
物件可新增警告
Declaration: {
bad: (decl, { result }) {
decl.warn(result, 'Deprecated property bad')
}
}
如果您的外掛程式依賴於另一個檔案,您可以將訊息附加到 result
以表示執行器(webpack、Gulp 等),當此檔案變更時,它們應該重新建置 CSS
AtRule: {
import: (atRule, { result }) {
const importedFile = parseImport(atRule)
result.messages.push({
type: 'dependency',
plugin: 'postcss-import',
file: importedFile,
parent: result.opts.from
})
}
}
如果相依性是目錄,您應該改用 dir-dependency
訊息類型
result.messages.push({
type: 'dir-dependency',
plugin: 'postcss-import',
dir: importedDir,
parent: result.opts.from
})
如果您發現語法錯誤(例如,未定義的客製化屬性),您可以擲回特殊錯誤
if (!variables[name]) {
throw decl.error(`Unknown variable ${name}`, { word: name })
}
步驟 5:對抗挫折
我討厭程式設計
我討厭程式設計
我討厭程式設計
它運作了!
我愛程式設計
即使是簡單的外掛程式,您也會遇到錯誤,而且至少需要 10 分鐘進行除錯。您可能會發現,簡單的原始構想在現實世界中無法運作,而且您需要變更所有內容。
別擔心。每個錯誤都是可以找到的,而且找到另一個解決方案可能會讓您的外掛程式變得更好。
從撰寫測試開始。外掛程式樣板在 index.test.js
中有一個測試範本。呼叫 npx jest
來測試您的外掛程式。
在您的文字編輯器中使用 Node.js 除錯器,或僅使用 console.log
來除錯程式碼。
PostCSS 社群可以協助您,因為我們都遇到過相同的問題。別害怕在 專屬頻道 中提問。
步驟 6:公開
當您的外掛程式準備好時,請在您的儲存庫中呼叫 npx clean-publish
。 clean-publish
是用來從 npm 套件中移除開發組態的工具。我們已將此工具新增到我們的樣板外掛程式中。
使用 @postcss
標籤撰寫關於你的新外掛程式(即使它很小)的推文。或在 [我們的聊天室] 中說明你的外掛程式。我們將協助你行銷。
將你的新外掛程式新增至 PostCSS 外掛程式目錄。