整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          如何編寫屬于自己的 PostCSS 8 插件?

          如何編寫屬于自己的 PostCSS 8 插件?

          者近期在將前端架構(gòu) webpack 升級(jí)到 5 時(shí),一些配套模塊也需要進(jìn)行升級(jí),其中包括了 css 處理模塊 PostCSS。舊版本使用的是 PostCSS 7,在升級(jí)至 PostCSS 8 的過程中,筆者發(fā)現(xiàn)部分插件前置依賴還是停留在 7 版本,且年久失修,在 PostCSS 8 中出現(xiàn)各種各樣的問題,無(wú)奈只能研究源碼,將目前部分舊版本插件升級(jí)至新版本。這里,筆者將升級(jí)插件的過程進(jìn)行簡(jiǎn)化和提煉,讓讀者自己也可以編寫一個(gè) PostCSS 8 插件。

          插件工作原理

          PostCSS 是一個(gè)允許使用 JS 插件轉(zhuǎn)換樣式的工具。開發(fā)者可以根據(jù)自己的實(shí)際需求,在編譯過程將指定 css 樣式進(jìn)行轉(zhuǎn)換和處理。目前 PostCSS 官方收錄插件有 200 多款,其中包括使用最廣泛的Autoprefixer自動(dòng)補(bǔ)全 css 前綴插件。

          PostCSS 和插件的工作原理其實(shí)很簡(jiǎn)單,就是先將 css 源碼轉(zhuǎn)換為 AST,插件基于轉(zhuǎn)換后 AST 的信息進(jìn)行個(gè)性化處理,最后 PostCSS 再將處理后的 AST 信息轉(zhuǎn)換為 css 源碼,完成 css 樣式轉(zhuǎn)換,其流程可以歸結(jié)為下圖:

          下面我們通過實(shí)際例子看看 PostCSS 會(huì)將 css 源碼轉(zhuǎn)換成的 AST 格式:

          const postcss=require('postcss')
          postcss().process(`
          .demo {
           font-size: 14px; /*this is a comment*/
          }
          `).then(result=> {
           console.log(result)
          })

          復(fù)制代碼

          代碼中直接引用 PostCSS,在不經(jīng)過任何插件的情況下將 css 源碼進(jìn)行轉(zhuǎn)換,AST 轉(zhuǎn)換結(jié)果如下:

          {
           "processor": {
           "version": "8.3.6",
           "plugins": []
           },
           "messages": [],
           "root": {
           "raws": {
           "semicolon": false,
           "after": "\n"
           },
           "type": "root",
           // ↓ nodes字段內(nèi)容重點(diǎn)關(guān)注
           "nodes": [
           {
           "raws": {
           "before": "\n",
           "between": " ",
           "semicolon": true,
           "after": "\n"
           },
           "type": "rule",
           "nodes": [
           {
           "raws": {
           "before": "\n ",
           "between": ": "
           },
           "type": "decl",
           "source": {
           "inputId": 0,
           "start": {
           "offset": 11,
           "line": 3,
           "column": 3
           },
           "end": {
           "offset": 26,
           "line": 3,
           "column": 18
           }
           },
           "prop": "font-size", // css屬性和值
           "value": "14px"
           },
           {
           "raws": {
           "before": " ",
           "left": "",
           "right": ""
           },
           "type": "comment", // 注釋類
           "source": {
           "inputId": 0,
           "start": {
           "offset": 28,
           "line": 3,
           "column": 20
           },
           "end": {
           "offset": 48,
           "line": 3,
           "column": 40
           }
           },
           "text": "this is a comment"
           }
           ],
           "source": {
           "inputId": 0,
           "start": {
           "offset": 1,
           "line": 2,
           "column": 1
           },
           "end": {
           "offset": 28,
           "line": 4,
           "column": 1
           }
           },
           "selector": ".demo", // 類名
           "lastEach": 1,
           "indexes": {}
           }
           ],
           "source": {
           "inputId": 0,
           "start": {
           "offset": 0,
           "line": 1,
           "column": 1
           }
           },
           "lastEach": 1,
           "indexes": {},
           "inputs": [
           {
           "hasBOM": false,
           "css": "\n.demo {\n font-size: 14px;\n}\n",
           "id": "<input css vi1Oew>"
           }
           ]
           },
           "opts": {},
           "css": "\n.demo {\n font-size: 14px;\n}\n"
          }

          復(fù)制代碼

          AST 對(duì)象中 nodes 字段里的內(nèi)容尤為重要,其中存儲(chǔ)了 css 源碼的關(guān)鍵字、注釋、源碼的起始、結(jié)束位置以及 css 的屬性和屬性值,類名使用selector存儲(chǔ),每個(gè)類下又存儲(chǔ)一個(gè) nodes 數(shù)組,該數(shù)組下存放的就是該類的屬性(prop)和屬性值(value)。那么插件就可以基于 AST 字段對(duì) css 屬性進(jìn)行修改,從而實(shí)現(xiàn) css 的轉(zhuǎn)換。

          PostCSS 插件格式規(guī)范及 API

          PostCSS 插件其實(shí)就是一個(gè) JS 對(duì)象,其基本形式和解析如下:

          module.exports=(opts={ })=> {
           // 此處可對(duì)插件配置opts進(jìn)行處理
           return {
           postcssPlugin: 'postcss-test', // 插件名字,以postcss-開頭
           
          Once (root, postcss) {
           // 此處root即為轉(zhuǎn)換后的AST,此方法轉(zhuǎn)換一次css將調(diào)用一次
           },
           
          Declaration (decl, postcss) {
           // postcss遍歷css樣式時(shí)調(diào)用,在這里可以快速獲得type為decl的節(jié)點(diǎn)(請(qǐng)參考第二節(jié)的AST對(duì)象)
           },
           
          Declaration: {
           color(decl, postcss) {
           // 可以進(jìn)一步獲得decl節(jié)點(diǎn)指定的屬性值,這里是獲得屬性為color的值
           }
           },
           
          Comment (comment, postcss) {
           // 可以快速訪問AST注釋節(jié)點(diǎn)(type為comment)
           },
           
          AtRule(atRule, postcss) {
           // 可以快速訪問css如@media,@import等@定義的節(jié)點(diǎn)(type為atRule)
           }
           
          }
          }
          module.exports.postcss=true

          復(fù)制代碼

          更多的 PostCSS 插件 API 可以詳細(xì)參考官方postcss8文檔,基本原理就是 PostCSS 會(huì)遍歷每一個(gè) css 樣式屬性值、注釋等節(jié)點(diǎn),之后開發(fā)者就可以針對(duì)個(gè)性需求對(duì)節(jié)點(diǎn)進(jìn)行處理即可。

          實(shí)際開發(fā)一個(gè) PostCSS 8 插件

          了解了 PostCSS 插件的格式和 API,我們將根據(jù)實(shí)際需求來(lái)開發(fā)一個(gè)簡(jiǎn)易的插件,有如下 css:

          .demo {
           font-size: 14px; /*this is a comment*/
           color: #ffffff;
          }

          復(fù)制代碼

          需求如下:

          1. 刪除 css 內(nèi)注釋
          2. 將所有顏色為十六進(jìn)制的#ffffff轉(zhuǎn)為 css 內(nèi)置的顏色變量white

          根據(jù)第三節(jié)的插件格式,本次開發(fā)只需使用Comment和Declaration接口即可:

          // plugin.js
          module.exports=(opts={ })=> {
           return {
           postcssPlugin: 'postcss-test',
           
          Declaration (decl, postcss) {
           if (decl.value==='#ffffff') {
           decl.value='white'
           }
           },
           
          Comment(comment) {
           comment.text=''
           }
           
          }
          }
          module.exports.postcss=true

          復(fù)制代碼

          在 PostCSS 中使用該插件:

          // index.js
          const plugin=require('./plugin.js')
          postcss([plugin]).process(`
          .demo {
           font-size: 14px; /*this is a comment*/
           color: #ffffff;
          }
          `).then(result=> {
           console.log(result.css)
          })

          復(fù)制代碼

          運(yùn)行結(jié)果如下:

          .demo {
           font-size: 14px; /**/
           color: white;
          }

          復(fù)制代碼

          可以看到,字體顏色值已經(jīng)成功做了轉(zhuǎn)換,注釋內(nèi)容已經(jīng)刪掉,但注釋標(biāo)識(shí)符還依舊存在,這是因?yàn)樽⑨尮?jié)點(diǎn)是包含/**/內(nèi)容存在的,只要 AST 里注釋節(jié)點(diǎn)還存在,最后 PostCSS 還原 AST 時(shí)還是會(huì)把這段內(nèi)容還原,要做到徹底刪掉注釋,需要對(duì) AST 的 nodes 字段進(jìn)行遍歷,將 type 為 comment 的節(jié)點(diǎn)進(jìn)行刪除,插件源碼修改如下:

          // plugin.js
          module.exports=(opts={ })=> {
           // Work with options here
           // https://postcss.org/api/#plugin
           return {
           postcssPlugin: 'postcss-test',
           
          Once (root, postcss) {
           // Transform CSS AST here
           root.nodes.forEach(node=> {
           if (node.type==='rule') {
           node.nodes.forEach((n, i)=> {
           if (n.type==='comment') {
           node.nodes.splice(i, 1)
           }
           })
           }
           })
           },
           
          
          Declaration (decl, postcss) {
           // The faster way to find Declaration node
           if (decl.value==='#ffffff') {
           decl.value='white'
           }
           }
           
          }
          }
          module.exports.postcss=true

          復(fù)制代碼

          重新執(zhí)行 PostCSS,結(jié)果如下,符合預(yù)期。

          .demo {
           font-size: 14px;
           color: white;
          }

          復(fù)制代碼

          插件開發(fā)注意事項(xiàng)

          通過實(shí)操開發(fā)可以看到,開發(fā)一個(gè) PostCSS 插件其實(shí)很簡(jiǎn)單,但在實(shí)際的插件開發(fā)中,開發(fā)者需要注意以下事項(xiàng):

          1.盡量使插件簡(jiǎn)單,使用者可以到手即用

          Build code that is short, simple, clear, and modular.

          盡量使你的插件和使用者代碼解耦,開放有限的 API,同時(shí)開發(fā)者在使用你的插件時(shí)從名字就可以知道插件的功能。這里推薦一個(gè)簡(jiǎn)單而優(yōu)雅的 PostCSS 插件postcss-focus,讀者可以從這個(gè)插件的源碼中體會(huì)這個(gè)設(shè)計(jì)理念。

          2.開發(fā)插件前確認(rèn)是否有現(xiàn)成的輪子

          如果你對(duì)自己的項(xiàng)目有個(gè)新點(diǎn)子,想自己開發(fā)一個(gè)插件去實(shí)現(xiàn),在開始寫代碼前,可以先到 PostCSS 官方注冊(cè)的插件列表中查看是否有符合自己需求的插件,避免重復(fù)造輪子。不過截止目前(2021.8),大部分插件依舊停留在 PostCSS 8 以下,雖然 PostCSS 8 已經(jīng)對(duì)舊版本插件做了處理,但在 AST 的解析處理上還是有差異,從實(shí)際使用過程中我就發(fā)現(xiàn) PostCss8 使用低版本插件會(huì)導(dǎo)致 AST 內(nèi)的source map丟失,因此目前而言完全兼容 PostCSS 8 的插件還需各位開發(fā)者去升級(jí)。

          從低版本 PostCSS 遷移

          升級(jí)你的 PostCSS 插件具體可以參考官方給出的升級(jí)指引。這里只對(duì)部分關(guān)鍵部分做下解釋:

          1.升級(jí) API

          • 將舊版module.exports=postcss.plugin(name, creator)替換為module.exports=creator;
          • 新版插件將直接返回一個(gè)對(duì)象,對(duì)象內(nèi)包含Once方法回調(diào);
          • 將原插件邏輯代碼轉(zhuǎn)移至Once方法內(nèi);
          • 插件源碼最后加上module.exports.postcss=true;

          具體示例如下。

          舊版插件:

          - module.exports=postcss.plugin('postcss-dark-theme-class', (opts={})=> {
          - checkOpts(opts)
          - return (root, result)=> {
           root.walkAtRules(atrule=> { … })
          - }
          - })

          復(fù)制代碼

          升級(jí)后插件:

          + module.exports=(opts={})=> {
          + checkOpts(opts)
          + return {
          + postcssPlugin: 'postcss-dark-theme-class',
          + Once (root, { result }) {
           root.walkAtRules(atrule=> { … })
          + }
          + }
          + }
          + module.exports.postcss=true

          復(fù)制代碼

          2.提取邏輯代碼至新版 API

          把邏輯代碼都放在Once回調(diào)內(nèi)還不夠優(yōu)雅,PostCSS 8 已經(jīng)實(shí)現(xiàn)了單個(gè) css 的代碼掃描,提供了Declaration(), Rule(), AtRule(), Comment() 等方法,舊版插件類似root.walkAtRules的方法就可以分別進(jìn)行重構(gòu),插件效率也會(huì)得到提升:

          module.exports={
           postcssPlugin: 'postcss-dark-theme-class',
          - Once (root) {
          - root.walkAtRules(atRule=> {
          - // Slow
          - })
          - }
          + AtRule (atRule) {
          + // Faster
          + }
           }
           module.exports.postcss=true

          復(fù)制代碼

          總結(jié)

          通過本文的介紹,讀者可以了解 PostCSS 8 工作的基本原理,根據(jù)具體需求快速開發(fā)一個(gè) PostCSS 8 插件,并在最后引用官方示例中介紹了如何快速升級(jí)舊版 PostCSS 插件。目前 PostCSS 8 還有大量還沒進(jìn)行升級(jí)兼容的 PostCSS 插件,希望讀者可以在閱讀本文后可以獲得啟發(fā),對(duì) PostCSS 8 的插件生態(tài)做出貢獻(xiàn)。

          nicode 聯(lián)盟(Unicode Consortium)

          Unicode 聯(lián)盟(Unicode Consortium)開發(fā)了 Unicode 標(biāo)準(zhǔn)(Unicode Standard)。他們的目標(biāo)是使用標(biāo)準(zhǔn)的 Unicode 轉(zhuǎn)換格式(即 UTF,全稱 Unicode Transformation Format)取代現(xiàn)有的字符集。

          Unicode 標(biāo)準(zhǔn)是一個(gè)成功的創(chuàng)舉,在 HTML、XML、Java、JavaScript、E-mail、ASP、PHP 中都得到實(shí)現(xiàn)。Unicode 標(biāo)準(zhǔn)也得到許多操作系統(tǒng)和所有現(xiàn)代瀏覽器的支持。

          Unicode 聯(lián)盟與領(lǐng)先的標(biāo)準(zhǔn)開發(fā)組織合作,這些組織有 ISO、W3C 和 ECMA。


          Unicode 字符集

          Unicode 可以由不同的字符集實(shí)現(xiàn)。最常用的編碼是 UTF-8 和 UTF-16:

          字符集描述
          UTF-8UTF8 中的字符可以是 1 到 4 字節(jié)長(zhǎng)。UTF-8 可以代表 Unicode 標(biāo)準(zhǔn)中的任何字符。UTF-8 向后兼容 ASCII。UTF-8 是電子郵件和網(wǎng)頁(yè)的首選編碼。
          UTF-1616 位 Unicode 轉(zhuǎn)換格式是一種可變長(zhǎng)度的 Unicode 字符編碼,能夠編碼整個(gè) Unicode 指令表。UTF-16 主要用于操作系統(tǒng)和環(huán)境,如 Microsoft Windows、Java 和 .NET。

          提示:Unicode 的前 128 個(gè)字符(與 ASCII 一一對(duì)應(yīng))使用一個(gè)與 ASCII二進(jìn)制值相同的八位組進(jìn)行編碼,使有效的 ASCII 文本在進(jìn)行 UTF-8 編碼時(shí)也是有效的。

          提示:所有的 HTML 4 處理器支持 UTF-8,所有的 HTML 5 和 XML 處理器支持 UTF-8 和 UTF-16!


          HTML5 標(biāo)準(zhǔn):Unicode UTF-8

          因?yàn)?ISO-8859 中字符集大小是有限的,且在多語(yǔ)言環(huán)境中不兼容,所以 Unicode 聯(lián)盟開發(fā)了 Unicode 標(biāo)準(zhǔn)。

          Unicode 標(biāo)準(zhǔn)覆蓋了(幾乎)所有的字符、標(biāo)點(diǎn)符號(hào)和符號(hào)。

          Unicode 使文本的處理、存儲(chǔ)和運(yùn)輸,獨(dú)立于平臺(tái)和語(yǔ)言。

          HTML-5 中默認(rèn)的字符編碼是 UTF-8。

          下面列出了一些 HTML5 支持的 UTF-8 字符集:

          字符集十進(jìn)制十六進(jìn)制
          C0 控制與基本的 Latin(C0 Controls and Basic Latin)0-1270000-007F
          C1 控制與 Latin-1 的補(bǔ)充(C1 Controls and Latin-1 Supplement)128-2550080-00FF
          Latin 擴(kuò)展 A(Latin Extended-A)256-3830100-017F
          Latin 擴(kuò)展 B(Latin Extended-B)384-5910180-024F

          如果 HTML5 網(wǎng)頁(yè)使用不同于 UTF-8 的字符,則需要在 <meta> 標(biāo)簽中指定,如下:

          實(shí)例

          <meta charset="ISO-8859-1">

          如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!

          ello大家好,今天廣州藍(lán)景跟大家分享一些html的使用技巧。

          1. 使用capture屬性打開設(shè)備攝像頭

          正如input標(biāo)簽具有email、text和password屬性一樣,我們也可以通過一些屬性打開移動(dòng)設(shè)備的攝像頭以捕獲圖像。

          那就是capture屬性,屬性值有兩個(gè):

          • user用于前置攝像頭
          • environment用于后置攝像頭
          <input type="file" capture="user" accept="image/*">
          

          2. 網(wǎng)站自動(dòng)刷新

          你可以在head標(biāo)簽中將網(wǎng)站設(shè)置為定時(shí)刷新!

          <head>
              <meta http-equiv="refresh" content="10">
          </head>
          

          此代碼段可以實(shí)現(xiàn)每10秒刷新一次網(wǎng)站。

          3. 激活拼寫檢查

          你可以使用HTML的spellcheck屬性并將其設(shè)置為true以激活拼寫檢查。使用lang屬性指定待檢查的語(yǔ)言。

          <input type="text" spellcheck="true" lang="en">
          

          這是一個(gè)標(biāo)準(zhǔn)屬性,得到了大多數(shù)瀏覽器的支持。

          4. 指定要上傳的文件類型

          你可以使用accept屬性在input標(biāo)簽中指定允許用戶上傳的文件類型。

          <input type="file" accept=".jpeg,.png">
          

          5. 阻止瀏覽器翻譯

          將translate屬性設(shè)置為no會(huì)阻止瀏覽器翻譯該內(nèi)容。如果你不想翻譯某個(gè)短語(yǔ)或單詞,例如logo、公司或品牌名稱,那就可以應(yīng)用這個(gè)屬性。

          <p translate="no">Brand name</p>
          

          6. 在input標(biāo)簽中輸入多個(gè)項(xiàng)目

          這可以通過multiple屬性來(lái)完成。

          <input type="file" multiple>
          

          適用于文件和電子郵件。如果是電子郵件,則可以用逗號(hào)分隔。

          7. 為視頻創(chuàng)建海報(bào)(縮略圖)

          使用poster屬性,我們可以在視頻加載時(shí),或者在用戶點(diǎn)擊播放按鈕之前,顯示指定的縮略圖。

          如果不指定圖片,則默認(rèn)使用視頻的第一幀作為縮略圖。

          <video poster="picture.png"></video>
          

          8. 點(diǎn)擊鏈接自動(dòng)下載

          如果你希望在單擊目標(biāo)資源的鏈接時(shí)下載特定資源,那就添加download屬性。

          <a href="image.png" download>
          

          今天就分享到這里,想要了解更多前端技術(shù)知識(shí),可以關(guān)注我們廣州藍(lán)景。


          主站蜘蛛池模板: 天堂va视频一区二区| 51视频国产精品一区二区| 一区二区三区四区无限乱码| 天天看高清无码一区二区三区| 久久久久人妻一区精品| 精品无码一区二区三区在线| 日本一区二区三区在线看| 亚洲A∨无码一区二区三区| 国产成人综合亚洲一区| 日韩一区二区电影| 日韩精品一区二区三区国语自制| 一夲道无码人妻精品一区二区| 国产精品第一区揄拍| 久久综合精品不卡一区二区| 精品一区二区三区无码免费直播 | 亚洲一区二区三区乱码在线欧洲| 国产精品538一区二区在线| 在线播放偷拍一区精品| 亚洲一区二区三区夜色 | 一区二区视频在线免费观看| AV无码精品一区二区三区| 人妻久久久一区二区三区| 亚洲AV无码一区东京热| 亚洲一区综合在线播放| 亚洲国产精品自在线一区二区| 亚洲熟妇av一区二区三区漫画| 亚洲片国产一区一级在线观看| 日韩精品一区二区三区不卡| 鲁大师成人一区二区三区| 无码人妻一区二区三区免费视频| 日本无卡码免费一区二区三区| 亚洲AV成人一区二区三区观看 | 亚洲熟妇无码一区二区三区| 亚洲AV无码一区二区三区牛牛| 天海翼一区二区三区高清视频| 免费一区二区视频| tom影院亚洲国产一区二区| 国产成人精品一区二区秒拍| 久久青青草原一区二区| 亚洲av无码天堂一区二区三区| 午夜天堂一区人妻|