[{"data":1,"prerenderedAt":1509},["ShallowReactive",2],{"\u002Fwriting\u002Fpostcss":3},{"id":4,"title":5,"body":6,"date":1498,"description":12,"extension":1499,"intro":1500,"meta":1501,"navigation":602,"path":1502,"seo":1503,"stem":1504,"tags":1505,"__hash__":1508},"writing\u002Fwriting\u002Fpostcss.md","每个人都该了解的 PostCSS",{"type":7,"value":8,"toc":1484},"minimark",[9,13,18,21,39,47,58,113,117,120,127,130,135,142,145,174,177,441,444,452,467,470,483,487,490,494,497,501,504,508,511,514,517,524,538,1133,1136,1139,1143,1167,1410,1413,1422,1425,1480],[10,11,12],"p",{},"PostCSS 是一项时常被提起的技术，但与它的知名度不相匹配的则是相当多的对 PostCSS 的误解。对于习惯了使用样式预处理器的我们而言，熟悉并且在一定程度上享受 Sass、Less 或 Stylus 提供的变量、嵌套和混合等程序化特性时，往往也就忽视了 CSS 领域的其他技术。对于 PostCSS，即便不使用它，对每一个编写 CSS 的人而言也都应当对 PostCSS 有基本的了解，认识它是什么、不是什么，以及在它的能力范围内能做到什么。",[14,15,17],"h2",{"id":16},"postcss","PostCSS？",[10,19,20],{},"PostCSS 不是一门语言，没有定义一种语法规则，也不是预处理器的替代方案。PostCSS 是一个用 JavaScript 编写的工具，提供转换 CSS 代码的能力。PostCSS 就像是 CSS 界的 Babel——本身不直接改变 CSS，而是提供一个解析、分析和重构 CSS 抽象语法树（AST）的平台。",[22,23,26],"callout",{"icon":24,"title":25},"Ban","PostCSS 不是什么",[27,28,29,33,36],"ul",{},[30,31,32],"li",{},"PostCSS 不是像 sass 或 less 那样的样式预处理器（style preprocessor），但可以借助 PostCSS 的能力实现类似的功能；",[30,34,35],{},"PostCSS 不是样式后处理器。尽管名字带有「Post」，但它并不局限于后处理。PostCSS 可以作用于开发时、构建时甚至运行时；",[30,37,38],{},"PostCSS 不是一种语言或单一工具，而是一个平台，由插件系统实现特定功能。",[10,40,41,42,46],{},"换句话说，PostCSS 的核心是一个CSS 解析器 + 插件系统。它接收 CSS 源码，解析成 AST，然后将 AST 交给一系列插件依次处理，最后将修改后的 AST 重新生成为 CSS 代码。PostCSS 可以读取样式表（很可能包含额外的语法和特殊的内容），处理它并输出标准的、可以被浏览器所接受的 CSS。任何 ",[43,44,45],"em",{},"类似于 CSS 的语法"," 的文件均可以被 PostCSS 所处理。",[10,48,49,50,57],{},"社区内已经有",[51,52,56],"a",{"href":53,"rel":54},"https:\u002F\u002Fpostcss.org\u002Fdocs\u002Fpostcss-plugins",[55],"nofollow","非常多的插件","，涵盖了几乎各个方向和使用场景。下面这些插件的名气甚至超过了 PostCSS 本身：",[27,59,60,68,76,84,97,105],{},[30,61,62,67],{},[51,63,66],{"href":64,"rel":65},"https:\u002F\u002Fgithub.com\u002Fpostcss\u002Fautoprefixer",[55],"Autoprefixer","：自动为部分样式规则添加浏览器前缀。",[30,69,70,75],{},[51,71,74],{"href":72,"rel":73},"https:\u002F\u002Fstylelint.io\u002F",[55],"Stylelint","：一个检查和修复 CSS 样式错误的工具。",[30,77,78,83],{},[51,79,82],{"href":80,"rel":81},"https:\u002F\u002Fcssnano.co\u002F",[55],"cssnano","：一个用于压缩 CSS 代码的工具。",[30,85,86,91,92,96],{},[51,87,90],{"href":88,"rel":89},"https:\u002F\u002Fgithub.com\u002Fpostcss\u002Fpostcss-import",[55],"postcss-import","：一个用于处理 CSS 中的",[93,94,95],"code",{},"@import","规则的插件。它将样式内联以优化加载速度。",[30,98,99,104],{},[51,100,103],{"href":101,"rel":102},"https:\u002F\u002Fgithub.com\u002Fcsstools\u002Fpostcss-preset-env",[55],"postcss-preset-env","：用于将现代 CSS 转为浏览器可以识别的规则的插件。",[30,106,107,112],{},[51,108,111],{"href":109,"rel":110},"https:\u002F\u002Ftailwindcss.com\u002F",[55],"TailwindCSS","：著名的原子化 CSS 框架，它提供了一套预定义的、可组合的类名，用于快速构建自定义的、响应式的用户界面。",[14,114,116],{"id":115},"工作原理从-css-到-ast再到-css","工作原理：从 CSS 到 AST，再到 CSS",[10,118,119],{},"这是一个来自于 PostCSS 文档中的图，展示了整个 PostCSS 的工作流程。",[10,121,122],{},[123,124],"img",{"alt":125,"src":126},"PostCSS 工作流程","\u002Fimg\u002Fpostcss\u002Fworkflow.png",[10,128,129],{},"大体上，PostCSS 使用 Parser 识别输入的源代码，并且创建一个可以描述源代码结构的对象；随后，PostCSS 将这个对象逐级传递给各个插件，每个插件都可以根据需要调整这个对象；最后，Stringifier 将这个对象重新生成为 CSS 代码。",[131,132,134],"h3",{"id":133},"tokenizer","Tokenizer",[10,136,137,138,141],{},"Tokenizer（或称为 Lexer，",[43,139,140],{},"“词法分析器”","）在语法解析中扮演了十分重要的角色。它将输入的字符串转换为由标记构成的列表。",[10,143,144],{},"例如，考虑下面的 CSS 作为输入",[146,147,152],"pre",{"className":148,"code":149,"language":150,"meta":151,"style":151},"language-css shiki shiki-themes houston",".className { color: #fff }\n","css","",[93,153,154],{"__ignoreMap":151},[155,156,159,163,167,171],"span",{"class":157,"line":158},"line",1,[155,160,162],{"class":161},"sb16X",".className",[155,164,166],{"class":165},"sLo_3"," { color: ",[155,168,170],{"class":169},"se2J0","#fff",[155,172,173],{"class":165}," }\n",[10,175,176],{},"PostCSS 得到的标记列表将是这样的：",[146,178,182],{"className":179,"code":180,"language":181,"meta":151,"style":151},"language-js shiki shiki-themes houston","[\n  [\"word\", \".className\", 1, 1, 1, 10]\n  [\"space\", \" \"]\n  [\"{\", \"{\", 1, 12]\n  [\"space\", \" \"]\n  [\"word\", \"color\", 1, 14, 1, 18]\n  [\":\", \":\", 1, 19]\n  [\"space\", \" \"]\n  [\"word\", \"#FFF\" , 1, 21, 1, 23]\n  [\";\", \";\", 1, 24]\n  [\"space\", \" \"]\n  [\"}\", \"}\", 1, 26]\n]\n","js",[93,183,184,189,225,240,263,276,308,331,344,377,400,413,436],{"__ignoreMap":151},[155,185,186],{"class":157,"line":158},[155,187,188],{"class":165},"[\n",[155,190,192,195,198,201,204,206,209,211,213,215,217,219,222],{"class":157,"line":191},2,[155,193,194],{"class":165},"  [",[155,196,197],{"class":169},"\"word\"",[155,199,200],{"class":165},", ",[155,202,203],{"class":169},"\".className\"",[155,205,200],{"class":165},[155,207,208],{"class":169},"1",[155,210,200],{"class":165},[155,212,208],{"class":169},[155,214,200],{"class":165},[155,216,208],{"class":169},[155,218,200],{"class":165},[155,220,221],{"class":169},"10",[155,223,224],{"class":165},"]\n",[155,226,228,230,233,235,238],{"class":157,"line":227},3,[155,229,194],{"class":165},[155,231,232],{"class":169},"\"space\"",[155,234,200],{"class":165},[155,236,237],{"class":169},"\" \"",[155,239,224],{"class":165},[155,241,243,245,248,250,252,254,256,258,261],{"class":157,"line":242},4,[155,244,194],{"class":165},[155,246,247],{"class":169},"\"{\"",[155,249,200],{"class":165},[155,251,247],{"class":169},[155,253,200],{"class":165},[155,255,208],{"class":169},[155,257,200],{"class":165},[155,259,260],{"class":169},"12",[155,262,224],{"class":165},[155,264,266,268,270,272,274],{"class":157,"line":265},5,[155,267,194],{"class":165},[155,269,232],{"class":169},[155,271,200],{"class":165},[155,273,237],{"class":169},[155,275,224],{"class":165},[155,277,279,281,283,285,288,290,292,294,297,299,301,303,306],{"class":157,"line":278},6,[155,280,194],{"class":165},[155,282,197],{"class":169},[155,284,200],{"class":165},[155,286,287],{"class":169},"\"color\"",[155,289,200],{"class":165},[155,291,208],{"class":169},[155,293,200],{"class":165},[155,295,296],{"class":169},"14",[155,298,200],{"class":165},[155,300,208],{"class":169},[155,302,200],{"class":165},[155,304,305],{"class":169},"18",[155,307,224],{"class":165},[155,309,311,313,316,318,320,322,324,326,329],{"class":157,"line":310},7,[155,312,194],{"class":165},[155,314,315],{"class":169},"\":\"",[155,317,200],{"class":165},[155,319,315],{"class":169},[155,321,200],{"class":165},[155,323,208],{"class":169},[155,325,200],{"class":165},[155,327,328],{"class":169},"19",[155,330,224],{"class":165},[155,332,334,336,338,340,342],{"class":157,"line":333},8,[155,335,194],{"class":165},[155,337,232],{"class":169},[155,339,200],{"class":165},[155,341,237],{"class":169},[155,343,224],{"class":165},[155,345,347,349,351,353,356,359,361,363,366,368,370,372,375],{"class":157,"line":346},9,[155,348,194],{"class":165},[155,350,197],{"class":169},[155,352,200],{"class":165},[155,354,355],{"class":169},"\"#FFF\"",[155,357,358],{"class":165}," , ",[155,360,208],{"class":169},[155,362,200],{"class":165},[155,364,365],{"class":169},"21",[155,367,200],{"class":165},[155,369,208],{"class":169},[155,371,200],{"class":165},[155,373,374],{"class":169},"23",[155,376,224],{"class":165},[155,378,380,382,385,387,389,391,393,395,398],{"class":157,"line":379},10,[155,381,194],{"class":165},[155,383,384],{"class":169},"\";\"",[155,386,200],{"class":165},[155,388,384],{"class":169},[155,390,200],{"class":165},[155,392,208],{"class":169},[155,394,200],{"class":165},[155,396,397],{"class":169},"24",[155,399,224],{"class":165},[155,401,403,405,407,409,411],{"class":157,"line":402},11,[155,404,194],{"class":165},[155,406,232],{"class":169},[155,408,200],{"class":165},[155,410,237],{"class":169},[155,412,224],{"class":165},[155,414,416,418,421,423,425,427,429,431,434],{"class":157,"line":415},12,[155,417,194],{"class":165},[155,419,420],{"class":169},"\"}\"",[155,422,200],{"class":165},[155,424,420],{"class":169},[155,426,200],{"class":165},[155,428,208],{"class":169},[155,430,200],{"class":165},[155,432,433],{"class":169},"26",[155,435,224],{"class":165},[155,437,439],{"class":157,"line":438},13,[155,440,224],{"class":165},[10,442,443],{},"对于每一个标记或单词，标记列表中均有一个子列表对其进行了描述。每个子列表的构成为：",[146,445,450],{"className":446,"code":448,"language":449},[447],"language-text","[标记类型, 标记内容, 行号, 列号, (结束行号, 结束列号)]\n","text",[93,451,448],{"__ignoreMap":151},[10,453,454,455,458,459,462,463,466],{},"其中，子列表下标 0 的位置标记了 token 的类型，包括词",[93,456,457],{},"word","、空格",[93,460,461],{},"space","、冒号",[93,464,465],{},":","等；下标 1 的位置则是 token 本身。对于标记位置，不部分 token 具有不止 1 个字符长度，因此包含额外的下标 4~5 的元素，用于指示 token 结束的位置。",[10,468,469],{},"标记化的实现方式和承载 token 的数据结构可以有其他选择，PostCSS 选择了看起来很“脏”的列表式。但事实上，标记化的过程将占据整个语法分析中约 90% 的时间，因此速度和性能至关重要，任何更复杂的高级构造（例如类）都会显著拖慢速度。",[22,471,474],{"icon":472,"title":473},"FilePenLine","扁平化语法结构",[10,475,476,477,482],{},"事实上，采用扁平结构进行语法分析的项目还有不少，扁平结构意味着不需要进行树形结构复杂的递归操作。用于构造富文本编辑器的 ",[51,478,481],{"href":479,"rel":480},"https:\u002F\u002Fprosemirror.net\u002F",[55],"prosemirror","就采用扁平序列存储表示文档内容的数据。依赖于扁平的数据结构可以更方便地实现诸如拆分段落、更改内容样式等操作。",[131,484,486],{"id":485},"parser","Parser",[10,488,489],{},"Parser（解释器）是 PostCSS 中对 CSS 进行语法分析的主要结构，它消费 Tokenizer 输出的 token，并构建 AST 供 PostCSS 的插件在随后进行处理。",[131,491,493],{"id":492},"processor","Processor",[10,495,496],{},"Processor（处理器）在 PostCSS 中负责初始化插件和执行语法转换。通过每个插件依次处理 AST，最终得到经过转换的树状结构。",[131,498,500],{"id":499},"stringifier","Stringifier",[10,502,503],{},"当 PostCSS 完成了语法转换后，经过插件处理得到的新的 AST 需要被转换为字符串并输出为新的 CSS 文件。Stringifier 负责遍历 AST 树，并输出字符串以实现这一最后的步骤。",[14,505,507],{"id":506},"插件postcss-的灵魂","插件：PostCSS 的灵魂",[10,509,510],{},"毫无疑问，单独安装的一份 PostCSS 并不能帮助到开发者。PostCSS 的魅力在于它提供了一整套工具和无限的想象力，我们可以编写自己的插件来实现一些有趣或有用的想法。",[131,512,513],{"id":513},"自动生成色板文档",[10,515,516],{},"在大型项目中，设计系统的颜色分散在各处。可以编写一个插件，提取所有用到的颜色，并且在 CSS 文件的末尾生成一份注释形式的报告。",[10,518,519,523],{},[520,521,522],"strong",{},"实现思路","：",[27,525,526,529,532,535],{},[30,527,528],{},"遍历所有声明；",[30,530,531],{},"如果属性值包含颜色，那么提取颜色的值（通过正则表达式匹配 RGB、HEX 或者 HSL 格式）；",[30,533,534],{},"收集并统计；",[30,536,537],{},"在 CSS 文件最后输出注释，列出各颜色及使用次数。",[146,539,541],{"className":179,"code":540,"language":181,"meta":151,"style":151},"export default postcss.plugin(\"postcss-palette-doc\", () => {\n  const paletteMap = new Map();\n\n  const toComment = () => {\n    let str = \"\\n * 色板统计 \\n\";\n    paletteMap.forEach((v, k) => {\n      str += ` * ${k}: 使用${v.length}次（${v.join(\", \")}）\\n`;\n    });\n    return str;\n  };\n\n  return (root) => {\n    root.walkRules((rule) => {\n      rule.walkDecls((decl) => {\n        const matched = decl.value.match(\u002F#[0-9a-f]{3,6}|rgba?\\([^)]+\\)|hsla?\\([^)]+\\)\u002Fgi);\n        if (matched && matched.length > 0) {\n          matched.forEach((color) => {\n            if (!paletteMap.has(color)) {\n              paletteMap.set(color, [rule.selector]);\n            } else {\n              paletteMap.get(color).push(rule.selector);\n            }\n          });\n        }\n      });\n    });\n\n    const comment = toComment();\n    root.append(postcss.comment({ text: comment }));\n  };\n});\n",[93,542,543,577,598,604,618,644,673,738,743,752,757,761,778,799,821,897,926,947,971,999,1010,1040,1046,1052,1058,1064,1069,1074,1090,1122,1127],{"__ignoreMap":151},[155,544,545,549,552,555,558,562,565,568,571,574],{"class":157,"line":158},[155,546,548],{"class":547},"sqF6k","export",[155,550,551],{"class":547}," default",[155,553,554],{"class":161}," postcss",[155,556,557],{"class":165},".",[155,559,561],{"class":560},"stHQG","plugin",[155,563,564],{"class":165},"(",[155,566,567],{"class":169},"\"postcss-palette-doc\"",[155,569,570],{"class":165},", () ",[155,572,573],{"class":547},"=>",[155,575,576],{"class":165}," {\n",[155,578,579,582,586,589,592,595],{"class":157,"line":191},[155,580,581],{"class":547},"  const",[155,583,585],{"class":584},"s8HWQ"," paletteMap",[155,587,588],{"class":165}," = ",[155,590,591],{"class":547},"new",[155,593,594],{"class":560}," Map",[155,596,597],{"class":165},"();\n",[155,599,600],{"class":157,"line":227},[155,601,603],{"emptyLinePlaceholder":602},true,"\n",[155,605,606,608,611,614,616],{"class":157,"line":242},[155,607,581],{"class":547},[155,609,610],{"class":560}," toComment",[155,612,613],{"class":165}," = () ",[155,615,573],{"class":547},[155,617,576],{"class":165},[155,619,620,623,626,628,631,634,637,639,641],{"class":157,"line":265},[155,621,622],{"class":547},"    let",[155,624,625],{"class":161}," str",[155,627,588],{"class":165},[155,629,630],{"class":169},"\"",[155,632,633],{"class":165},"\\n",[155,635,636],{"class":169}," * 色板统计 ",[155,638,633],{"class":165},[155,640,630],{"class":169},[155,642,643],{"class":165},";\n",[155,645,646,649,651,654,657,661,663,666,669,671],{"class":157,"line":278},[155,647,648],{"class":161},"    paletteMap",[155,650,557],{"class":165},[155,652,653],{"class":560},"forEach",[155,655,656],{"class":165},"((",[155,658,660],{"class":659},"smONe","v",[155,662,200],{"class":165},[155,664,665],{"class":659},"k",[155,667,668],{"class":165},") ",[155,670,573],{"class":547},[155,672,576],{"class":165},[155,674,675,678,681,684,687,689,692,695,697,699,701,704,706,709,711,713,715,718,720,723,726,728,731,733,736],{"class":157,"line":310},[155,676,677],{"class":161},"      str",[155,679,680],{"class":165}," += ",[155,682,683],{"class":169},"` * ",[155,685,686],{"class":547},"${",[155,688,665],{"class":161},[155,690,691],{"class":547},"}",[155,693,694],{"class":169},": 使用",[155,696,686],{"class":547},[155,698,660],{"class":161},[155,700,557],{"class":165},[155,702,703],{"class":161},"length",[155,705,691],{"class":547},[155,707,708],{"class":169},"次（",[155,710,686],{"class":547},[155,712,660],{"class":161},[155,714,557],{"class":165},[155,716,717],{"class":560},"join",[155,719,564],{"class":165},[155,721,722],{"class":169},"\", \"",[155,724,725],{"class":165},")",[155,727,691],{"class":547},[155,729,730],{"class":169},"）",[155,732,633],{"class":165},[155,734,735],{"class":169},"`",[155,737,643],{"class":165},[155,739,740],{"class":157,"line":333},[155,741,742],{"class":165},"    });\n",[155,744,745,748,750],{"class":157,"line":346},[155,746,747],{"class":547},"    return",[155,749,625],{"class":161},[155,751,643],{"class":165},[155,753,754],{"class":157,"line":379},[155,755,756],{"class":165},"  };\n",[155,758,759],{"class":157,"line":402},[155,760,603],{"emptyLinePlaceholder":602},[155,762,763,766,769,772,774,776],{"class":157,"line":415},[155,764,765],{"class":547},"  return",[155,767,768],{"class":165}," (",[155,770,771],{"class":659},"root",[155,773,668],{"class":165},[155,775,573],{"class":547},[155,777,576],{"class":165},[155,779,780,783,785,788,790,793,795,797],{"class":157,"line":438},[155,781,782],{"class":161},"    root",[155,784,557],{"class":165},[155,786,787],{"class":560},"walkRules",[155,789,656],{"class":165},[155,791,792],{"class":659},"rule",[155,794,668],{"class":165},[155,796,573],{"class":547},[155,798,576],{"class":165},[155,800,802,805,807,810,812,815,817,819],{"class":157,"line":801},14,[155,803,804],{"class":161},"      rule",[155,806,557],{"class":165},[155,808,809],{"class":560},"walkDecls",[155,811,656],{"class":165},[155,813,814],{"class":659},"decl",[155,816,668],{"class":165},[155,818,573],{"class":547},[155,820,576],{"class":165},[155,822,824,827,830,832,834,836,839,841,844,846,849,852,855,858,861,864,867,870,873,876,878,880,882,884,886,889,891,894],{"class":157,"line":823},15,[155,825,826],{"class":547},"        const",[155,828,829],{"class":584}," matched",[155,831,588],{"class":165},[155,833,814],{"class":161},[155,835,557],{"class":165},[155,837,838],{"class":161},"value",[155,840,557],{"class":165},[155,842,843],{"class":560},"match",[155,845,564],{"class":165},[155,847,848],{"class":169},"\u002F",[155,850,851],{"class":165},"#",[155,853,854],{"class":169},"[0-9a-f]{3,6}",[155,856,857],{"class":165},"|rgba",[155,859,860],{"class":169},"?",[155,862,863],{"class":165},"\\(",[155,865,866],{"class":169},"[",[155,868,869],{"class":165},"^",[155,871,872],{"class":169},")]+",[155,874,875],{"class":165},"\\)|hsla",[155,877,860],{"class":169},[155,879,863],{"class":165},[155,881,866],{"class":169},[155,883,869],{"class":165},[155,885,872],{"class":169},[155,887,888],{"class":165},"\\)",[155,890,848],{"class":169},[155,892,893],{"class":547},"gi",[155,895,896],{"class":165},");\n",[155,898,900,903,905,908,911,913,915,917,920,923],{"class":157,"line":899},16,[155,901,902],{"class":547},"        if",[155,904,768],{"class":165},[155,906,907],{"class":161},"matched",[155,909,910],{"class":165}," && ",[155,912,907],{"class":161},[155,914,557],{"class":165},[155,916,703],{"class":161},[155,918,919],{"class":165}," > ",[155,921,922],{"class":169},"0",[155,924,925],{"class":165},") {\n",[155,927,929,932,934,936,938,941,943,945],{"class":157,"line":928},17,[155,930,931],{"class":161},"          matched",[155,933,557],{"class":165},[155,935,653],{"class":560},[155,937,656],{"class":165},[155,939,940],{"class":659},"color",[155,942,668],{"class":165},[155,944,573],{"class":547},[155,946,576],{"class":165},[155,948,950,953,956,959,961,964,966,968],{"class":157,"line":949},18,[155,951,952],{"class":547},"            if",[155,954,955],{"class":165}," (!",[155,957,958],{"class":161},"paletteMap",[155,960,557],{"class":165},[155,962,963],{"class":560},"has",[155,965,564],{"class":165},[155,967,940],{"class":161},[155,969,970],{"class":165},")) {\n",[155,972,974,977,979,982,984,986,989,991,993,996],{"class":157,"line":973},19,[155,975,976],{"class":161},"              paletteMap",[155,978,557],{"class":165},[155,980,981],{"class":560},"set",[155,983,564],{"class":165},[155,985,940],{"class":161},[155,987,988],{"class":165},", [",[155,990,792],{"class":161},[155,992,557],{"class":165},[155,994,995],{"class":161},"selector",[155,997,998],{"class":165},"]);\n",[155,1000,1002,1005,1008],{"class":157,"line":1001},20,[155,1003,1004],{"class":165},"            } ",[155,1006,1007],{"class":547},"else",[155,1009,576],{"class":165},[155,1011,1013,1015,1017,1020,1022,1024,1027,1030,1032,1034,1036,1038],{"class":157,"line":1012},21,[155,1014,976],{"class":161},[155,1016,557],{"class":165},[155,1018,1019],{"class":560},"get",[155,1021,564],{"class":165},[155,1023,940],{"class":161},[155,1025,1026],{"class":165},").",[155,1028,1029],{"class":560},"push",[155,1031,564],{"class":165},[155,1033,792],{"class":161},[155,1035,557],{"class":165},[155,1037,995],{"class":161},[155,1039,896],{"class":165},[155,1041,1043],{"class":157,"line":1042},22,[155,1044,1045],{"class":165},"            }\n",[155,1047,1049],{"class":157,"line":1048},23,[155,1050,1051],{"class":165},"          });\n",[155,1053,1055],{"class":157,"line":1054},24,[155,1056,1057],{"class":165},"        }\n",[155,1059,1061],{"class":157,"line":1060},25,[155,1062,1063],{"class":165},"      });\n",[155,1065,1067],{"class":157,"line":1066},26,[155,1068,742],{"class":165},[155,1070,1072],{"class":157,"line":1071},27,[155,1073,603],{"emptyLinePlaceholder":602},[155,1075,1077,1080,1083,1085,1088],{"class":157,"line":1076},28,[155,1078,1079],{"class":547},"    const",[155,1081,1082],{"class":584}," comment",[155,1084,588],{"class":165},[155,1086,1087],{"class":560},"toComment",[155,1089,597],{"class":165},[155,1091,1093,1095,1097,1100,1102,1104,1106,1109,1112,1114,1117,1119],{"class":157,"line":1092},29,[155,1094,782],{"class":161},[155,1096,557],{"class":165},[155,1098,1099],{"class":560},"append",[155,1101,564],{"class":165},[155,1103,16],{"class":161},[155,1105,557],{"class":165},[155,1107,1108],{"class":560},"comment",[155,1110,1111],{"class":165},"({ ",[155,1113,449],{"class":161},[155,1115,1116],{"class":165},": ",[155,1118,1108],{"class":161},[155,1120,1121],{"class":165}," }));\n",[155,1123,1125],{"class":157,"line":1124},30,[155,1126,756],{"class":165},[155,1128,1130],{"class":157,"line":1129},31,[155,1131,1132],{"class":165},"});\n",[131,1134,1135],{"id":1135},"mixin",[10,1137,1138],{},"Mixin 指可以被混入其他声明规则中的 CSS 代码，sass 等预处理器支持使用这种方式进行样式的复用。",[10,1140,1141,523],{},[520,1142,522],{},[27,1144,1145,1152,1158],{},[30,1146,1147,1148,1151],{},"使用",[93,1149,1150],{},"@mixin","定义一个 mixin；",[30,1153,1147,1154,1157],{},[93,1155,1156],{},"@include","调用 mixin；",[30,1159,1160,1161,1163,1164,1166],{},"将",[93,1162,1150],{},"定义的样式规则插入到",[93,1165,1156],{},"的位置。",[146,1168,1170],{"className":179,"code":1169,"language":181,"meta":151,"style":151},"export default postcss.plugin(\"postcss-mixin\", (options = {}) => {\n  const mixins = new Map()\n  return {\n    \u002F\u002F 对于所有 @~:\n    AtRule: {\n      \u002F\u002F 对于所有 @mixin:\n      mixin: (node) => {\n        mixins.set(node.params, node.nodes);\n        node.remove();\n      },\n      \u002F\u002F 对于所有 @include\n      include: (node) => {\n        const name = node.params;\n        if (mixins.get(name)) {\n          node.replaceWith(mixins.get(name));\n        }\n      }\n    }\n  };\n});\n",[93,1171,1172,1202,1218,1224,1230,1238,1243,1260,1289,1301,1306,1311,1326,1343,1363,1388,1392,1397,1402,1406],{"__ignoreMap":151},[155,1173,1174,1176,1178,1180,1182,1184,1186,1189,1192,1195,1198,1200],{"class":157,"line":158},[155,1175,548],{"class":547},[155,1177,551],{"class":547},[155,1179,554],{"class":161},[155,1181,557],{"class":165},[155,1183,561],{"class":560},[155,1185,564],{"class":165},[155,1187,1188],{"class":169},"\"postcss-mixin\"",[155,1190,1191],{"class":165},", (",[155,1193,1194],{"class":659},"options",[155,1196,1197],{"class":165}," = {}) ",[155,1199,573],{"class":547},[155,1201,576],{"class":165},[155,1203,1204,1206,1209,1211,1213,1215],{"class":157,"line":191},[155,1205,581],{"class":547},[155,1207,1208],{"class":584}," mixins",[155,1210,588],{"class":165},[155,1212,591],{"class":547},[155,1214,594],{"class":560},[155,1216,1217],{"class":165},"()\n",[155,1219,1220,1222],{"class":157,"line":227},[155,1221,765],{"class":547},[155,1223,576],{"class":165},[155,1225,1226],{"class":157,"line":242},[155,1227,1229],{"class":1228},"sy89t","    \u002F\u002F 对于所有 @~:\n",[155,1231,1232,1235],{"class":157,"line":265},[155,1233,1234],{"class":161},"    AtRule",[155,1236,1237],{"class":165},": {\n",[155,1239,1240],{"class":157,"line":278},[155,1241,1242],{"class":1228},"      \u002F\u002F 对于所有 @mixin:\n",[155,1244,1245,1248,1251,1254,1256,1258],{"class":157,"line":310},[155,1246,1247],{"class":560},"      mixin",[155,1249,1250],{"class":165},": (",[155,1252,1253],{"class":659},"node",[155,1255,668],{"class":165},[155,1257,573],{"class":547},[155,1259,576],{"class":165},[155,1261,1262,1265,1267,1269,1271,1273,1275,1278,1280,1282,1284,1287],{"class":157,"line":333},[155,1263,1264],{"class":161},"        mixins",[155,1266,557],{"class":165},[155,1268,981],{"class":560},[155,1270,564],{"class":165},[155,1272,1253],{"class":161},[155,1274,557],{"class":165},[155,1276,1277],{"class":161},"params",[155,1279,200],{"class":165},[155,1281,1253],{"class":161},[155,1283,557],{"class":165},[155,1285,1286],{"class":161},"nodes",[155,1288,896],{"class":165},[155,1290,1291,1294,1296,1299],{"class":157,"line":346},[155,1292,1293],{"class":161},"        node",[155,1295,557],{"class":165},[155,1297,1298],{"class":560},"remove",[155,1300,597],{"class":165},[155,1302,1303],{"class":157,"line":379},[155,1304,1305],{"class":165},"      },\n",[155,1307,1308],{"class":157,"line":402},[155,1309,1310],{"class":1228},"      \u002F\u002F 对于所有 @include\n",[155,1312,1313,1316,1318,1320,1322,1324],{"class":157,"line":415},[155,1314,1315],{"class":560},"      include",[155,1317,1250],{"class":165},[155,1319,1253],{"class":659},[155,1321,668],{"class":165},[155,1323,573],{"class":547},[155,1325,576],{"class":165},[155,1327,1328,1330,1333,1335,1337,1339,1341],{"class":157,"line":438},[155,1329,826],{"class":547},[155,1331,1332],{"class":584}," name",[155,1334,588],{"class":165},[155,1336,1253],{"class":161},[155,1338,557],{"class":165},[155,1340,1277],{"class":161},[155,1342,643],{"class":165},[155,1344,1345,1347,1349,1352,1354,1356,1358,1361],{"class":157,"line":801},[155,1346,902],{"class":547},[155,1348,768],{"class":165},[155,1350,1351],{"class":161},"mixins",[155,1353,557],{"class":165},[155,1355,1019],{"class":560},[155,1357,564],{"class":165},[155,1359,1360],{"class":161},"name",[155,1362,970],{"class":165},[155,1364,1365,1368,1370,1373,1375,1377,1379,1381,1383,1385],{"class":157,"line":823},[155,1366,1367],{"class":161},"          node",[155,1369,557],{"class":165},[155,1371,1372],{"class":560},"replaceWith",[155,1374,564],{"class":165},[155,1376,1351],{"class":161},[155,1378,557],{"class":165},[155,1380,1019],{"class":560},[155,1382,564],{"class":165},[155,1384,1360],{"class":161},[155,1386,1387],{"class":165},"));\n",[155,1389,1390],{"class":157,"line":899},[155,1391,1057],{"class":165},[155,1393,1394],{"class":157,"line":928},[155,1395,1396],{"class":165},"      }\n",[155,1398,1399],{"class":157,"line":949},[155,1400,1401],{"class":165},"    }\n",[155,1403,1404],{"class":157,"line":973},[155,1405,756],{"class":165},[155,1407,1408],{"class":157,"line":1001},[155,1409,1132],{"class":165},[131,1411,1412],{"id":1412},"插件设计准则",[10,1414,1415,1416,1421],{},"PostCSS 的插件开发十分简单，大众对于处理 CSS 的需求又是五花八门的，因此 PostCSS 插件种类繁多，包括既可以被正式用于生产项目的例子（前面已然列举了不少），也包括单纯为了好玩而被创造的（例如",[51,1417,1420],{"href":1418,"rel":1419},"https:\u002F\u002Fgithub.com\u002Fdp-lewis\u002Fpostcss-australian-stylesheets",[55],"PostCSS Australian Stylesheets","）。",[10,1423,1424],{},"对于需要从头开发一个插件的用户来说，有一些需要遵守的准则。",[27,1426,1427,1433,1442,1445,1456,1470],{},[30,1428,1429,1432],{},[520,1430,1431],{},"Do one thing, and do it well."," PostCSS 要求不创造“多目的性”的插件，而是将多个更小的、目标单一的插件集成为一个插件包。",[30,1434,1160,1435,1437,1438,1441],{},[93,1436,16],{},"添加到插件的",[93,1439,1440],{},"peerDependencies","中。",[30,1443,1444],{},"必须至少使用最新的 node.js LTS 测试插件。",[30,1446,1447,1448,1451,1452,1455],{},"在所有场合尽量使用异步方法。例如，使用",[93,1449,1450],{},"fs.readFile","代替",[93,1453,1454],{},"fs.readFileSync","。",[30,1457,1458,1459,1461,1462,1465,1466,1469],{},"使用更快的节点扫描方式。例如，如果明确知道要处理的样式规则是属性",[93,1460,940],{},"，那么使用",[93,1463,1464],{},"Declaration: { color: \u002F**\u002F }","要比调用",[93,1467,1468],{},"walkDeclaration","快得多。",[30,1471,1147,1472,1475,1476,1479],{},[93,1473,1474],{},"node.error","报告错误，使用",[93,1477,1478],{},"result.warn","输出警告。",[1481,1482,1483],"style",{},"html pre.shiki code .sb16X, html code.shiki .sb16X{--shiki-default:#4BF3C8}html pre.shiki code .sLo_3, html code.shiki .sLo_3{--shiki-default:#EEF0F9}html pre.shiki code .se2J0, html code.shiki .se2J0{--shiki-default:#FFD493}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sqF6k, html code.shiki .sqF6k{--shiki-default:#54B9FF}html pre.shiki code .stHQG, html code.shiki .stHQG{--shiki-default:#00DAEF}html pre.shiki code .s8HWQ, html code.shiki .s8HWQ{--shiki-default:#ACAFFF}html pre.shiki code .smONe, html code.shiki .smONe{--shiki-default:#4BF3C8;--shiki-default-font-style:italic}html pre.shiki code .sy89t, html code.shiki .sy89t{--shiki-default:#EEF0F98F;--shiki-default-font-style:italic}",{"title":151,"searchDepth":191,"depth":191,"links":1485},[1486,1487,1493],{"id":16,"depth":191,"text":17},{"id":115,"depth":191,"text":116,"children":1488},[1489,1490,1491,1492],{"id":133,"depth":227,"text":134},{"id":485,"depth":227,"text":486},{"id":492,"depth":227,"text":493},{"id":499,"depth":227,"text":500},{"id":506,"depth":191,"text":507,"children":1494},[1495,1496,1497],{"id":513,"depth":227,"text":513},{"id":1135,"depth":227,"text":1135},{"id":1412,"depth":227,"text":1412},"2021.12.18","md","每一个编写 CSS 的人都应该了解 PostCSS，它是什么、不是什么，以及在它的能力范围内能做到什么",{},"\u002Fwriting\u002Fpostcss",{"title":5,"description":12},"writing\u002Fpostcss",[1506,1507],"Engineering","CSS","Xy_MMEi8Vbws3pM1QRUYgIpH3G5FTjOMan7D1QbKmbU",1774950409696]