日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
零基礎(chǔ)理解PostCSS的主流程

本文適用于所有前端開發(fā)人員。文章會(huì)介紹 PostCSS 的主功能實(shí)現(xiàn)原理,不是介紹 api,也不會(huì)介紹所有功能的原理,如果有需要了解全部功能或者查閱 API,可查看官方文檔:https://postcss.org/api/。

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對(duì)這個(gè)行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡(jiǎn)單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:主機(jī)域名、網(wǎng)站空間、營(yíng)銷軟件、網(wǎng)站建設(shè)、烏拉特后網(wǎng)站維護(hù)、網(wǎng)站推廣。

什么是 PostCSS

官網(wǎng)說:“PostCSS,一個(gè)使用 JavaScript 來處理CSS的框架”。這句話高度概括了 PostCSS 的作用,但是太抽象了。按我理解,PostCSS 主要做了三件事:

  • parse:把 CSS 文件的字符串解析成抽象語(yǔ)法樹(Abstract Syntax Tree)的框架,解析過程中會(huì)檢查 CSS 語(yǔ)法是否正確,不正確會(huì)給出錯(cuò)誤提示。
  • runPlugin: 執(zhí)行插件函數(shù)。PostCSS 本身不處理任何具體任務(wù),它提供了以特定屬性或者規(guī)則命名的事件。有特定功能的插件(如 autoprefixer、CSS Modules)會(huì)注冊(cè)事件監(jiān)聽器。PostCSS 會(huì)在這個(gè)階段,重新掃描 AST,執(zhí)行注冊(cè)的監(jiān)聽器函數(shù)。
  • generate: 插件對(duì) AST 處理后,PostCSS 把處理過的 AST 對(duì)象轉(zhuǎn)成 CSS string。

「如果沒有插件」,那么初始傳入的 CSS string 和 generate 生成的 CSS string 是一樣的。由此可見,PostCSS 本身并不處理任何具體的任務(wù),只有當(dāng)我們?yōu)槠涓郊痈鞣N插件之后,它才具有實(shí)用性。

下面分別詳細(xì)分析三個(gè)階段做的事。

第一階段:parse

CSS 語(yǔ)法簡(jiǎn)述

CSS 規(guī)則集(rule-set)由選擇器和聲明塊組成:

  • 選擇器指向您需要設(shè)置樣式的 HTML 元素。
  • 聲明塊包含一條或多條用分號(hào)分隔的聲明。
  • 每條聲明都包含一個(gè) CSS 屬性名稱和一個(gè)值,以冒號(hào)分隔。
  • 多條 CSS 聲明用分號(hào)分隔,聲明塊用花括號(hào)括起來。

五類對(duì)象AST 用五類對(duì)象描述 CSS 語(yǔ)法。這里舉個(gè)具體的例子,再打印出對(duì)應(yīng)的 AST 結(jié)果,對(duì)照了解 AST 五類對(duì)象和 CSS 語(yǔ)法的對(duì)應(yīng)關(guān)系。

app.css 文件中寫如下內(nèi)容:

@import url('./app-02.css');

.container {
color: red;
}


Declaration 對(duì)象

Declaration 對(duì)象用來描述 CSS 中的每一條聲明語(yǔ)句。

  • type 標(biāo)記當(dāng)前對(duì)象的類型
  • parent 記錄父對(duì)象的實(shí)例
  • prop 記錄聲明中的屬性名
  • value 記錄聲明中的值
  • raws 字段記錄聲明前的字符串、聲明屬性和值之間的符號(hào)的字符串
  • 其余字段解釋見代碼中的注釋。

上邊 CSS 文件中的color: red;會(huì)被描述成如下對(duì)象:

{
parent: Rule, // 外層的選擇器被轉(zhuǎn)譯成 Rule 對(duì)象,是當(dāng)前聲明對(duì)象的 parent
prop: "color", // prop 字段記錄聲明的屬性
raws: { // raws 字段記錄聲明前、后的字符串,聲明屬性和值之間的字符串,以及前邊語(yǔ)句是否分號(hào)結(jié)束。
before: '\n ', // raws.before 字段記錄聲明前的字符串
between: ': ', // raws.between 字段記錄聲明屬性和值之間的字符串
},
source: { // source 字段記錄聲明語(yǔ)句的開始、結(jié)束位置,以及當(dāng)前文件的信息
start: { offset: 45, column: 3, line: 4 },
end: { offset: 55, column: 13, line: 4 },
input: Input {
css: '@import url('./app-02.css');\n\n.container {\n color: red;\n}',
file: '/Users/admin/temp/postcss/app.css',
hasBOM: false,
Symbol(fromOffsetCache): [0, 29, 30, 43, 57]
}
},
Symbol('isClean'): false, // Symbol(isClean) 字段默認(rèn)值都是 false,用于記錄當(dāng)前對(duì)象關(guān)聯(lián)的 plugin 是否執(zhí)行。plugin 會(huì)在后續(xù)解釋
Symbol('my'): true, // Symbol(my) 字段默認(rèn)值都是 true,用于記錄當(dāng)前對(duì)象是否是對(duì)應(yīng)對(duì)象的實(shí)例,如果不是,可以根據(jù)類型把對(duì)象的屬性設(shè)置為普通對(duì)象的 prototype 屬性
type: 'decl', // type 記錄對(duì)象類型,是個(gè)枚舉值,聲明語(yǔ)句的 type 固定是 decl
value: "red" // value 字段記錄聲明的值
}


每個(gè)字段的含義和功能已經(jīng)以注釋的形式進(jìn)行了解釋。

Rule 對(duì)象

Rule 對(duì)象是描述選擇器的。

  • type 記錄對(duì)象的類型
  • parent 記錄父對(duì)象的實(shí)例
  • nodes 記錄子對(duì)象的實(shí)例
  • selector 記錄選擇器的字符串
  • raws 記錄選擇器前的字符串、選擇器和大括號(hào)之間的字符串、最后一個(gè)聲明和結(jié)束大括號(hào)之間的字符串
  • 其余字段解釋見代碼中的注釋。

上邊 app.css 文件中.container經(jīng)過 postcss 轉(zhuǎn)譯后的對(duì)象是(每個(gè)字段的含義和功能已經(jīng)以注釋的形式進(jìn)行了解釋):

{
nodes: [Declaration], // nodes 記錄包含關(guān)系,Rule 對(duì)象包含 Declaration 對(duì)象
parent: Root, // 根對(duì)象是 Root 對(duì)象,是當(dāng)前聲明對(duì)象的 parent
raws: { // raws 字段記錄如下
before: '\n\n', // raws.before 字段記錄選擇器前的字符串
between: ' ', // raws.between 字段記錄選擇器和大括號(hào)之間的字符串
semicolon: true, // raws.semicolon 字段記錄前置聲明語(yǔ)句是正常分號(hào)結(jié)束
after: '\n' // raws.after 字段記錄最后一個(gè)聲明和結(jié)束大括號(hào)之間的字符串
},
selector:'.container', // selector 記錄 selector
source: { // source 字段記錄選擇器語(yǔ)句的開始、結(jié)束位置,以及當(dāng)前文件的信息
start: { offset: 30, column: 1, line: 3 },
input: Input {
css: '@import url('./app-02.css');\n\n.container {\n color: red;\n}',
file: '/Users/admin/temp/postcss/app.css',
hasBOM: false,
Symbol(fromOffsetCache): [0, 29, 30, 43, 57]
},
end: { offset: 57, column: 1, line: 5 }
},
Symbol('isClean'): false, // Symbol(isClean) 字段默認(rèn)值都是 false,用于記錄當(dāng)前對(duì)象關(guān)聯(lián)的 plugin 是否執(zhí)行。plugin 會(huì)在后續(xù)解釋
Symbol('my'): true, // Symbol(my) 字段默認(rèn)值都是 true,用于記錄當(dāng)前對(duì)象是否是對(duì)應(yīng)對(duì)象的實(shí)例,如果不是,可以根據(jù)類型把對(duì)象的屬性設(shè)置為普通對(duì)象的 prototype
type: 'rule' // type 記錄對(duì)象類型,是個(gè)枚舉值,聲明語(yǔ)句的 type 固定是 rule
}

Root 對(duì)象

Root 對(duì)象是 AST 對(duì)象的根對(duì)象。

  • type 記錄當(dāng)前對(duì)象的類型
  • nodes 屬性記錄子節(jié)點(diǎn)對(duì)應(yīng)對(duì)象的實(shí)例。

上邊 app.css 文件中 root 對(duì)象是(每個(gè)字段的含義和功能已經(jīng)以注釋的形式進(jìn)行了解釋):

{
nodes: [AtRule, Rule], // nodes 記錄子對(duì)象(選擇器和 @開頭的對(duì)象),AtRule 對(duì)象會(huì)在后邊提到
raws: { // raws 字段記錄如下
semicolon: false, // raws.semicolon 最后是否是分號(hào)結(jié)束
after: '' // raws.after 最后的空字符串
},
source: { // source 字段記錄根目錄語(yǔ)句的開始,以及當(dāng)前文件的信息
start: { offset: 0, column: 1, line: 1 },
input: Input {
css: '@import url('./app-02.css');\n\n.container {\n color: red;\n}',
file: '/Users/admin/temp/postcss/app.css',
hasBOM: false,
Symbol(fromOffsetCache): [0, 29, 30, 43, 57]
}
},
Symbol('isClean'): false, // Symbol(isClean) 字段默認(rèn)值都是 false,用于記錄當(dāng)前對(duì)象關(guān)聯(lián)的 plugin 是否執(zhí)行。plugin 會(huì)在后續(xù)解釋
Symbol('my'): true, // Symbol(my) 字段默認(rèn)值都是 true,用于記錄當(dāng)前對(duì)象是否是對(duì)應(yīng)對(duì)象的實(shí)例,如果不是,可以根據(jù)類型把對(duì)象的屬性設(shè)置為普通對(duì)象的 prototype
type: 'root' // type 記錄對(duì)象類型,是個(gè)枚舉值,聲明語(yǔ)句的 type 固定是 root
}

AtRule 對(duì)象

CSS 中除了選擇器,還有一類語(yǔ)法是 @ 開頭的,例如 @import、@keyframes、@font-face,PostCSS 把這類語(yǔ)法解析成 AtRule 對(duì)象。

  • type 記錄當(dāng)前對(duì)象的類型
  • parent 記錄當(dāng)前對(duì)象的父對(duì)象
  • name 記錄@緊跟著的單詞
  • params 記錄 name 值

例如 @import url("./app-02.css"); 將被解析成如下對(duì)象:

{
name: "import", // name 記錄 @ 緊跟著的單詞
params: "url('./app-02.css')", // params 記錄 name 值
parent: Root, // parent 記錄父對(duì)象
raws: { // raws 字段記錄如下
before: '', // raws.before 記錄 @語(yǔ)句前的空字符串
between: '', // raws.between 記錄 name 和 { 之間的空字符串
afterName: '', // raws.afterName 記錄 name 和 @ 語(yǔ)句之間的空字符串
after: '', // raws.after 記錄大括號(hào)和上一個(gè) rule 之間的空字符串
semicolon: false // raws.semicolon 上一個(gè)規(guī)則是否是分號(hào)結(jié)束
},
source: { // source 字段記錄@語(yǔ)句的開始,以及當(dāng)前文件的信息
start: { offset: 0, column: 1, line: 1 },
end: { offset: 27, column: 28, line: 1 },
input: Input {
css: '@import url('./app-02.css');\n\n.container {\n color: red;\n}',
file: '/Users/admin/temp/postcss/app.css',
hasBOM: false,
Symbol(fromOffsetCache): [0, 29, 30, 43, 57]
}
},
Symbol('isClean'): false, // Symbol(isClean) 字段默認(rèn)值都是 false,用于記錄當(dāng)前對(duì)象關(guān)聯(lián)的 plugin 是否執(zhí)行。plugin 會(huì)在后續(xù)解釋
Symbol('my'): true, // Symbol(my) 字段默認(rèn)值都是 true,用于記錄當(dāng)前對(duì)象是否是對(duì)應(yīng)對(duì)象的實(shí)例,如果不是,可以根據(jù)類型把對(duì)象的屬性設(shè)置為普通對(duì)象的 prototype
type: 'atrule' // type 記錄對(duì)象類型,是個(gè)枚舉值,聲明語(yǔ)句的 type 固定是 atrule
}

Comment 對(duì)象

css 文件中的注釋被解析成 Comment 對(duì)象。text 字段記錄注釋內(nèi)容。/* 你好 */被解析成:

{
parent: Root, // parent 記錄父對(duì)象
raws: { // raws 字段記錄如下
before: '', // raws.before 記錄注釋語(yǔ)句前的空字符串
left: ' ', // raws.left 記錄注釋語(yǔ)句左側(cè)的空字符串
right: ' ' // raws.right 記錄注釋語(yǔ)句右側(cè)的空字符串
},
source: { // source 字段記錄注釋語(yǔ)句的開始、結(jié)束位置,以及當(dāng)前文件的信息
start: {…}, input: Input, end: {…}
},
Symbol('isClean'): false, // Symbol(isClean) 字段默認(rèn)值都是 false,用于記錄當(dāng)前對(duì)象關(guān)聯(lián)的 plugin 是否執(zhí)行。plugin 會(huì)在后續(xù)解釋
Symbol('my'): true, // Symbol(my) 字段默認(rèn)值都是 true,用于記錄當(dāng)前對(duì)象是否是對(duì)應(yīng)對(duì)象的實(shí)例,如果不是,可以根據(jù)類型把對(duì)象的屬性設(shè)置為普通對(duì)象的 prototype
text: '你好', // text 記錄注釋內(nèi)容
type: 'comment' // type 記錄對(duì)象類型,是個(gè)枚舉值,聲明語(yǔ)句的 type 固定是 comment
}

圖解五類對(duì)象之間的繼承關(guān)系

從上一段可以知道,CSS 被解析成 Declaration、Rule、Root、AtRule、Comment 對(duì)象。這些對(duì)象有很多公共方法,PostCSS 用了面向?qū)ο蟮睦^承思想,把公共方法和公共屬性提取到了父類中。

Root、Rule、AtRule 都是可以有子節(jié)點(diǎn)的,都有 nodes 屬性,他們?nèi)齻€(gè)繼承自 Container 類,對(duì) nodes 的操作方法都寫在 Container 類中。Container、Declaration、Comment 繼承自 Node 類,所有對(duì)象都有 Symbol('isClean')、Symbol('my')、raws、source、type 屬性,都有toString()、error()等方法,這些屬性和方法都定義在 Node 類中。

Container、Node 是用來提取公共屬性和方法,不會(huì)生成他們的實(shí)例。

五個(gè)類之間的繼承關(guān)系如下圖所示:

圖中沒有窮舉類的方法,好奇的同學(xué)可以看直接看源碼文件: https://github.com/postcss/postcss/tree/main/lib 。

把 CSS 語(yǔ)法解析成 AST 對(duì)象的具體算法

算法對(duì)應(yīng)源碼中位置是:postcss/lib/parser.js中的parse方法,代碼量不大,可自行查看。

第二階段:runPlugin

PostCSS 本身并不處理任何具體的任務(wù),只有當(dāng)我們?yōu)槠涓郊痈鞣N插件之后,它才具有實(shí)用性。

PostCSS 在把 CSS string 解析成 AST 對(duì)象后,會(huì)掃描一邊 AST 對(duì)象,每一種 AST 的對(duì)象都可以有對(duì)應(yīng)的監(jiān)聽器。在遍歷到某類型的對(duì)象時(shí),如果有對(duì)象的監(jiān)聽器,就會(huì)執(zhí)行其監(jiān)聽器。

第一類監(jiān)聽器

PostCSS 提供的「以特定屬性或者規(guī)則命名」的事件監(jiān)聽器,如下:

CHILDREAN 代表子節(jié)點(diǎn)的事件監(jiān)聽器。

// root
['Root', CHILDREN, 'RootExit']

// AtRule
['AtRule', 'AtRule-import', CHILDREN, 'AtRuleExit', 'AtRuleExit-import']

// Rule
['Rule', CHILDREN, 'RuleExit']

// Declaration
['Declaration', 'Declaration-color', 'DeclarationExit', 'DeclarationExit-color']

// Comment
['Comment', 'CommentExit']

PostCSS 以深度優(yōu)先的方式遍歷 AST 樹。

  • 遍歷到 Root 根對(duì)象,第一步會(huì)執(zhí)行所有插件注冊(cè)的 Root 事件監(jiān)聽器,第二步檢查 Root 是否有子對(duì)象,如果有,則遍歷子對(duì)象,執(zhí)行子對(duì)象對(duì)應(yīng)的事件監(jiān)聽器;如果沒有子對(duì)象,則直接進(jìn)入第三步,第三步會(huì)執(zhí)行所有插件注冊(cè)的 RootExit 事件監(jiān)聽器。插件注冊(cè)的 Root、RootExit 事件的監(jiān)聽器只能是函數(shù)。函數(shù)的第一個(gè)參數(shù)是當(dāng)前訪問的 AST 的 Root 對(duì)象,第二個(gè)參數(shù)是 postcss 的 Result 對(duì)象和一些其他屬性,通過 Result 對(duì)象可以獲取 css string、opts 等信息。
{
Root: (rootNode, helps) => {},
RootExit: (rootNode, helps) => {}
}
  • 遍歷到 Rule 對(duì)象,則和訪問 Root 根對(duì)象是一樣的邏輯,先執(zhí)行所有插件注冊(cè)的 Rule 事件監(jiān)聽器,再遍歷子對(duì)象,最后執(zhí)行所有插件注冊(cè)的 RuleExit 事件監(jiān)聽器。插件注冊(cè)的 Rule、RuleExit 事件的監(jiān)聽器只能是函數(shù)。
{
Rule: (ruleNode, helps) => {},
RuleExit: (ruleNode, helps) => {}
}
  • 遍歷到 AtRule 對(duì)象。插件注冊(cè)的 AtRule 的事件監(jiān)聽器可以是函數(shù),也可以是對(duì)象。對(duì)象類型的監(jiān)聽器,對(duì)象屬性的 key 是 AtRule 對(duì)象的 name 值,value 是函數(shù)。AtRuleExit 是一樣的邏輯。事件的執(zhí)行順序是:['AtRule', 'AtRule-import', CHILDREN, 'AtRuleExit', 'AtRuleExit-import']。CHILDREAN 代表子節(jié)點(diǎn)的事件。``` // 函數(shù) { AtRule: (atRuleNode, helps) => {} }
// 對(duì)象
{
AtRule: {
import: (atRuleNode, helps) => {},
keyframes: (atRuleNode, helps) => {}
}
}

遍歷到 Declaration 對(duì)象。插件注冊(cè)的 Declaration 的事件監(jiān)聽器可以是函數(shù),也可以是對(duì)象,對(duì)象屬性的 key 是 Declaration 對(duì)象的 prop 值,value 是函數(shù)。DeclarationExitExit 是一樣的邏輯。事件的執(zhí)行順序是:['Declaration', 'Declaration-color', 'DeclarationExit', 'DeclarationExit-color']。Declaration 沒有子對(duì)象,只需要執(zhí)行當(dāng)前對(duì)象的事件,不需要深度執(zhí)行子對(duì)象的事件。

// 函數(shù)
{
Declaration: (declarationNode, helps) => {}
}

// 對(duì)象
{
Declaration: {
color: (declarationNode, helps) => {},
border: (declarationNode, helps) => {}
}
}

遍歷到 Comment 對(duì)象。依次執(zhí)行所有插件注冊(cè)的 Comment 事件監(jiān)聽器,再執(zhí)行所有插件注冊(cè)的 CommentExit 事件監(jiān)聽器。

第二類監(jiān)聽器

除以特定屬性或者規(guī)則命名的事件監(jiān)聽器,PostCSS 還有以下四個(gè):

{
postcssPlugin: string,
prepare: (result) => {},
Once: (root, helps) => {},
OnceExit: (root, helps) => {},
}

PostCSS 插件事件的整體執(zhí)行是:[prepare, Once, ...一類事件,OnceExit],postcssPlugin 是插件名稱,不是事件監(jiān)聽器。

  • postcssPlugin:字符串類型,插件的名字,在插件執(zhí)行報(bào)錯(cuò),提示用戶是哪個(gè)插件報(bào)錯(cuò)了。
  • prepare:函數(shù)類型,prepare 是最先執(zhí)行的,在所有事件執(zhí)行前執(zhí)行的,插件多個(gè)監(jiān)聽器間共享數(shù)據(jù)時(shí)使用。prepare 的入?yún)⑹?Result 對(duì)象,返回值是監(jiān)聽器對(duì)象,通過 Result 對(duì)象可以獲取 css string、opts 等信息。
{
postcssPlugin: "PLUGIN NAME",
prepare(result) {
const variables = {};
return {
Declaration(node) {
if (node.variable) {
variables[node.prop] = node.value;
}
},
OnceExit() {
console.log(variables);
},
};
},
};

Once:函數(shù)類型,在 prepare 后,一類事件前執(zhí)行,Once 只會(huì)執(zhí)行一次。

{
Once: (root, helps) => {}
}
  • OnceExit: 函數(shù)類型,在一類事件后執(zhí)行,OnceExit 只會(huì)執(zhí)行一次。

插件源碼截圖

此時(shí)再看市面上流行的基于 postcss 的工具,有沒有醍醐灌頂?

autoprefixer

postcss-import-parser

postcss-modules

postcss-modules

插件有哪些?

基于 postcss 的插件有很多,可查閱:https://github.com/postcss/postcss/blob/main/docs/plugins.md。

第三階段:generate

generate 的過程依舊是以深度優(yōu)先的方式遍歷 AST 對(duì)象,針對(duì)不同的實(shí)例對(duì)象進(jìn)行字符串的拼接。算法對(duì)應(yīng)源碼中位置是:postcss/lib/stringifier.js中的stringify方法,代碼量不大,可自行查看。


網(wǎng)站名稱:零基礎(chǔ)理解PostCSS的主流程
標(biāo)題來源:http://m.5511xx.com/article/dheogio.html