)
Sass 提案解析At-Rule 名称中的插值Interpolation in At-Rule Names【免费下载链接】sassSass makes CSS fun!项目地址: https://gitcode.com/gh_mirrors/sa/sass导读本篇文章基于 Sass 官方提案 accepted/at-rule-interpolation.mdDraft 1对应 Issue #429完整解读在 at-rule规则名称中使用#{}插值这一语法特性的设计动机、文法定义与运行时语义。读完本文你将掌握为什么插值名称的 at-rule 一律按未知规则UnknownAtRule处理、唯一的例外keyframes如何被特殊照顾、该特性如何用于动态生成带厂商前缀的 at-rule以及它与 Sass 现有语法InterpolatedIdentifier、InterpolatedValue之间的承接关系。提案背景与目标在 Sass 中插值interpolation是#{}语法允许把 SassScript 表达式的求值结果拼接到标识符、字符串、选择器等文本片段中。此前插值可以出现在 at-rule 的值value部分但 at-rule 的名称本身即之后的标识符是固定的字面量。本提案的目标是允许任何 at-rule 在其名称中包含插值例如m#{$vendor}media { ... }其核心应用场景是动态生成带厂商前缀的 at-rule 名称。由于浏览器厂商常通过-webkit-、-moz-、-ms-、-o-等前缀扩展 CSS 特性开发者希望用一段 Sass 代码统一输出多个前缀版本的规则例如$prefixes: -webkit-, -moz-, ; each $prefix in $prefixes { #{$prefix}keyframes fade-in { ... } }设计决策插值名称统一按未知 at-rule处理提案在 Design Decisions 一节明确了关键取舍任何 at-rule 都可以在名称中包含插值。插值会自动导致它被解析为未知 at-ruleunknown at-rule即使插值解析后的名称是 Sass 已知的规则。唯一的例外是keyframes它仍然允许包含10%之类的 keyframes 选择器子规则。为什么不重新解析已知规则理论上可以让 Sass 认识的 CSS at-rule如media、supports即使在名称由插值生成时也被特殊对待。但提案明确指出这会带来极大的实现复杂度实现需要具备在运行时重新解析这些 at-rule 值的能力收益不明确at-rule 插值主要用于添加厂商前缀而 Sass 有特殊运行时支持的media和supports恰好不使用厂商前缀因此没有为此付出复杂度的价值。运行时行为当一个 at-rule 被求值时先对其名称求值产出一个不带引号的字符串unquoted string作为生成 at-rule 的名称再检查该生成名称是否是需要特殊运行时处理的 at-rule在运行时触发特殊处理的只有keyframesmedia、supports等规则是在解析期parse-time被检测的。由此得出一个直接推论m#{ed}ia会被当作未知 at-rule而不会被当作媒体查询规则处理——因为media的特殊语义绑定在解析期而不是运行时。语法UnknownAtRule 产物的替换本提案定义了UnknownAtRule产物的替换文法与现有语法见 spec/at-rules/unknown.md相比唯一区别是名称部分可以使用插值UnknownAtRule :: InterpolatedIdentifier InterpolatedValue? ({ Statements })?文法要点之后不允许出现空白与所有语句一致不带块block的 UnknownAtRule 必须用分号与其他语句分隔InterpolatedIdentifier与InterpolatedValue均为可插值产物详见下文源码佐证。与现有 UnknownAtRule 文法的关系在 spec/at-rules/unknown.md 中当前规范对未知 at-rule 的文法定义为UnknownAtRule :: InterpolatedIdentifier¹ InterpolatedValue? Block? | function² InterpolatedIdentifier³ InterpolatedValue? Block其中 1名称不得与 Sass 定义的其他 at-rule 重名2function大小写不敏感匹配3必须以--开头。本提案即是在此基础上明确名称可以被插值并进一步规定插值名称一律走未知规则路径。提案原文特别注明这与之前的语法相同只是 at-rule 的名称可以被插值。InterpolatedIdentifier 与 InterpolatedValue提案中引用的两个非终结符来自 Sass 核心语法spec/syntax.mdInterpolatedIdentifier :: (ident-token | -? Interpolation) (Name | Interpolation)*其含义是一个可插值标识符由一个 CSS ident-token或可选的-加插值开头后跟任意多个 Name 片段或插值组成各组成部分之间不允许出现空白。InterpolatedValue对应 Sass 的InterpolatedAnyValue与 CSS 的any-value一致唯一区别是当解析到#{时会解析一个必须紧跟}的 Expression。此外spec/syntax.md 中的Consuming an Interpolated Identifier算法给出了解析器层面的具体步骤从-#{、#{或普通标识符开始随后循环消费#{、标识符码点或\转义最终返回由字符串与表达式组成的组件序列。运行时语义求值、拼接与特殊分支综合提案与 spec/at-rules/unknown.md 的语义描述一个带插值名称的未知 at-rule 在运行时按以下流程执行求值名称对InterpolatedIdentifier求值产出不带引号的字符串name求值值若存在InterpolatedValue对其求值得到value构造 CSS 节点创建一个名为name、值为value的 CSS 未知 at-rule定位父节点取当前样式规则current style rule、keyframe 块或 at-rule 中最近的作为父节点有 Statements 时父节点不存在 → 追加到当前模块的 CSS 输出父节点是样式规则且名称是font-face或其去前缀名unprefixed name为keyframes→ 追加到当前模块的 CSS 输出否则 → 先追加到父节点的父节点再复制一个不含子节点的父节点副本追加到 css 中该副本随后成为新的 current style rule然后逐个求值其子 Statements无 Statements 时必要时先拆分当前父节点splitting the current parent if necessary再追加到父节点或当前模块的 CSS 输出。其中关键的一步是第 5 条中的去前缀判断unprefixed它引用 spec/syntax.md 对 vendor prefix 的定义——前缀是-开头、后跟一个或多个非-码点、再跟-的子串且-之后必须还有文本剩余部分即无前缀标识符。keyframes唯一的运行时特殊处理为什么偏偏是keyframes因为它承载的子规则内容是选择器如0%、100%、from、to而这些内容只有在 keyframes 上下文里才合法。当-webkit-keyframes之类的名称由插值生成后运行时仍需将其内部内容按 keyframes 选择器解析。在 spec/style-rules.md 的样式规则语义中可以看到这一分支若父节点是名称去掉厂商前缀后为keyframes的未知 at-rule则子选择器文本会按keyframe selector解析追加一个 keyframe 块到父节点并结束当前样式规则的求值若父节点是 keyframe 块则抛出错误在 keyframes 内不允许再嵌套普通样式规则。这一机制保证了#{$prefix}keyframes生成的-webkit-keyframes、-moz-keyframes等规则内部仍能正确识别10%、from这类 keyframes 选择器。与 media、supports 的对比解析期 vs 运行时特性media/supports插值名称的未知 at-rule检测时机解析期parse-time运行时runtime特殊运行时处理有如嵌套媒体查询提升到顶层无除keyframes厂商前缀不使用主要用途插值名称结果m#{ed}ia不被识别为媒体规则一律按 UnknownAtRule 处理以media为例Sass 为其提供了特殊支持媒体查询会被解析两次第一次在解析 Sass 样式表时允许包含 SassScript 与插值第二次将求值结果作为纯 CSS 解析以实现嵌套媒体查询向顶层提升等能力见 spec/at-rules/media.md。这些特殊支持全部建立在解析期识别media的前提之上因此m#{ed}ia无法享受这些语义只能落入未知 at-rule 的通用路径。综合示例动态生成厂商前缀规则结合以上全部语义一个完整的实战用例是把前缀抽成变量循环输出多版本 at-rule$vendors: (-webkit-, -moz-, -ms-, ); each $vendor in $vendors { #{$vendor}keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } }编译行为分析每次循环#{$vendor}keyframes的名称经插值求值成为不带引号的字符串如-webkit-keyframes由于名称含插值一律按未知 at-rule 处理运行时检查发现其去前缀名keyframes命中唯一特殊分支于是块内的from/to/10%等选择器按 keyframe selector 解析并正确生成 keyframe 块最终输出四组独立的keyframes规则分别带-webkit-、-moz-、-ms-前缀及无前缀版本。再对比一个反例若把media写成m#{ed}ia由于媒体规则的特殊语义绑定在解析期该写法只会被当作一个名为media的未知 at-rule 原样输出不会触发嵌套媒体查询提升等 Sass 特有行为。总结本提案以最小文法改动UnknownAtRule的名称支持InterpolatedIdentifier为 Sass 解锁了 at-rule 名称插值能力同时通过插值名称一律视为未知 at-rule的设计将实现复杂度控制在可接受范围内唯一的运行时特殊分支给了keyframes恰好覆盖了厂商前缀使用场景中最需要动态化的动画规则而media、supports这类无需前缀的规则则完全不需要引入昂贵的运行时重解析机制。对于想要输出多前缀 CSS 的工具链与样式库作者而言这是一个成本极低、收益明确的语法扩展。延伸阅读仓库内相关资料提案全文accepted/at-rule-interpolation.md未知 at-rule 的现行文法与语义spec/at-rules/unknown.mdInterpolatedIdentifier文法与 vendor prefix 定义spec/syntax.mdkeyframes 特殊处理的运行时分支spec/style-rules.mdmedia的双重解析机制spec/at-rules/media.md样式表拆分父节点的过程spec/stylesheet.md【免费下载链接】sassSass makes CSS fun!项目地址: https://gitcode.com/gh_mirrors/sa/sass创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考