Sass JavaScript Calculation API 完整指南:在 JS API 中构建与使用 calc() / min() / max() / clamp() 计算类型

发布时间:2026/9/21 17:02:16
Sass JavaScript Calculation API 完整指南:在 JS API 中构建与使用 calc() / min() / max() / clamp() 计算类型 前端【免费下载链接】sassSass makes CSS fun!项目地址https://gitcode.com/gh_mirrors/sa/sass点击查看免费下载导读本文以 Sass 仓库中的 JavaScript Calculation API 草案Draft 3.1为骨架系统讲解如何在 Sass 的 JavaScript API 中表示、构建和操作 CSS 的calc()、min()、max()、clamp()等计算表达式。你将掌握SassCalculation的四个静态工厂方法、CalculationValue联合类型、CalculationOperation与CalculationInterpolation两个值对象以及Options.functions中自定义函数返回计算值时底层会发生什么——从而能够编写出返回计算值、参与编译期简化的真实自定义 Sass 函数。背景计算类型从 Sass 语言侧走向 JS APICSS 的calc()语法早已存在但 Sass 历史上一直把它当作不透明字符串处理括号内几乎任何 token 都被接受最终求值为一个未加引号的字符串想在calc()里使用 Sass 变量必须依赖插值。随着 CSS 数学函数的普及Sass 通过 First-Classcalc()提案 引入了一种全新的数据类型——calculation计算它表示编译期无法完全解析、需要交给浏览器求值的数学表达式如calc(10% 5px)同时允许这些表达式在后续的数学函数中优雅组合。calc(1px 10px)会被化简为数字11px而calc(1px 10%)则保持为计算类型。本草案 的定位非常明确把这套计算类型暴露到 JavaScript API 中让 JS 侧的自定义函数custom functions能够接收、创建并返回计算值。配套的变更记录见 calculation-api.changes.md计算类型的完整语言侧规范见 spec/types/calculation.md。设计决策为什么不在构造时立即化简草案的核心设计决策Design Decisions / Simplification值得首先理解。设计者曾考虑在计算对象被构造时急切化简eagerly simplify以对齐 Sass 语言内部的行为。但这对没有直接访问编译器逻辑能力的 API 实现提出了难题——典型的例子是Node.js embedded host它跨进程与编译器通信如果要本地实现化简逻辑代码复杂且容易在不同实现之间产生细微的不一致broad surface area for subtle cross-implementation incompatibilities。虽然可以通过在 embedded protocol 中增加显式请求来绕开但这又会与 JS 严格区分异步调用跨进程边界与同步调用本 API 属于同步的特性产生冲突。最终决定是化简只发生在自定义函数的边界custom function boundary而不是计算对象构造时。也就是说JS 侧构造出的计算对象保持未化简状态只有当它作为自定义函数的返回值跨过边界进入 Sass 时才执行化简详见下文Options.functions一节。API 概览与依赖import {List, ValueObject} from immutable; import {Value, SassNumber, SassString} from ../spec/js-api/value;API 依赖 immutable.js 的List与ValueObject接口List用于min/max的多参数以及SassCalculation.arguments属性ValueObject来自 immutable.js要求实现equals()与hashCode()保证CalculationOperation、CalculationInterpolation可以安全地放入 immutable 容器如List、Map、Set遵循值语义而非引用语义。实际发布类型定义与仓库中的 js-api-doc/value/calculation.d.ts 基本一致完整规范版见 spec/js-api/value/calculation.d.ts.md。CalculationValue计算参数的合法取值CalculationValue定义了可以作为SassCalculation参数的所有类型export type CalculationValue | SassNumber | SassCalculation | SassString | CalculationOperation | CalculationInterpolation;与语言侧内部类型Number | UnquotedString | CalculationOperation | Calculation见 spec/types/calculation.md相比JS API 版更宽泛SassString同时覆盖带引号与不带引号的字符串并额外允许CalculationInterpolation。语言规范中强调只有calculation-safe的表达式子集函数表达式、括号表达式、*乘积、数字、变量、插值标识符等才能作为计算参数而 JS API 的类型系统从外部对这一约束做了结构化保证。SassCalculation计算的 JS 侧表示SassCalculation继承自Value是 JS API 对 Sass 计算类型的完整表示export class SassCalculation extends Value {注意JS API 中计算不会被急切化简。这也意味着未化简的计算不等于它被化简后得到的数字——即SassCalculation.calc(sass.number(2))与sass.number(2)是不相等的两个值。internal与所有Value子类一样SassCalculation拥有一个私有internal字段指向语言侧真实的 Sass 计算对象见 spec/js-api/value/index.d.ts.md 中关于Value.internal的通用约定。该字段仅用于规范描述在 JS 中不可见。静态工厂calc/min/max三个工厂分别构造对应名字的计算static calc(argument: CalculationValue): SassCalculation; static min( arguments: CalculationValue[] | ListCalculationValue ): SassCalculation; static max( arguments: CalculationValue[] | ListCalculationValue ): SassCalculation;行为约定calc(argument)若argument是带引号的SassString抛错否则返回名字为calc、唯一参数为argument的计算。min(...arguments)若任一参数是带引号的SassString抛错否则返回名字为min、以arguments为参数列表的计算。参数可传入普通数组或 immutable 的ListCalculationValue。max(...arguments)规则与min完全对称构造名字为max的计算。静态工厂clampclamp的参数数量是可变的因此约束也最多static clamp( min: CalculationValue, value?: CalculationValue, max?: CalculationValue ): SassCalculation;若min、value或max中任何一个即文档中的 min, max, or clamp 三处是带引号的SassString抛错若value为undefined而max不为undefined抛错——不允许出现跳过中间参数却提供最后一个参数的非法形态若value或max为undefined且min与value都既不是SassString也不是CalculationInterpolation抛错——这确保了省略参数时必须有字符串/插值来兜底例如clamp(var(--x))这种把多个值塞进单个var()的写法返回名字为clamp的计算参数为min、value、max中所有非undefined的项。Draft 3 曾专门调整clamp允许把逗号分隔的min值解析为value与max的合法输入Draft 3.1 则进一步收窄并澄清了多参数场景下的行为同时把引号字符串不得存在的检查下沉到CalculationOperation的构造函数中从而保证嵌套结构中传递性地不会出现带引号字符串。只读访问器name与argumentsget name(): string; get arguments(): ListCalculationValue;name返回内部计算的名称字段即calc、min、max或clamp之一语言侧名称一律小写见 spec/types/calculation.md 中name 为call名称的小写值的规则arguments返回内部计算的参数列表类型为ListCalculationValue与 immutable 生态无缝衔接。这两个访问器与语言侧 First-Classcalc()提案 中新增的meta.calc-name()、meta.calc-args()内省函数一一对应构成语言内省与JS API 访问两条平行的只读路径。CalculationOperator与CalculationOperation二元运算节点export type CalculationOperator | - | * | /;CalculationOperator定义了计算中允许出现的全部二元运算符。CalculationOperation是这些运算在 JS API 中的值对象表示export class CalculationOperation implements ValueObject { constructor( operator: CalculationOperator, left: CalculationValue, right: CalculationValue ); get operator(): CalculationOperator; get left(): CalculationValue; get right(): CalculationValue; equals(other: unknown): boolean; hashCode(): number; }关键语义构造函数若left或right是带引号的SassString抛错随后按参数名设置三个字段并返回实例。Draft 3.1 特意把引号检查放在这里而不是让每个SassCalculation工厂做传递性检查——这样只要构造链上每一层CalculationOperation都通过检查整体结构就保证不含带引号字符串。只读字段operator、left、right分别对应内部 SassCalculationOperation的字段内部类型定义见 spec/types/calculation.md。equals(other: unknown)判断内部计算操作与other.internal在 Sass 中是否相等。参数类型为unknown这是 Draft 3.1 为对齐 immutable.js 类型声明而做的调整。hashCode()对任何按equals相等的两个CalculationOperation返回相同数字。它不要求对不等的对象给出不同哈希但哈希重叠过多会损害容器性能。这保证了对象作为List/Set等不可变容器键时行为正确。语言侧的序列化规则进一步解释了这类节点的输出形态spec/types/calculation.md序列化CalculationOperation时若运算符是*或/且左侧是/-操作则左操作数加括号若运算符是*或-且右侧是/-操作、或运算符是/且右侧是任意操作或带单位的不合法数字则右操作数加括号以保持 CSS 中正确的运算优先级。CalculationInterpolation括号包裹的字符串注入export class CalculationInterpolation implements ValueObject { constructor(value: string); get value(): string; equals(other: unknown): boolean; hashCode(): number; }CalculationInterpolation表示通过插值注入到计算中的字符串构造函数用value参数创建一个内部字段为未加引号 Sass 字符串、文本为( value )的对象value访问器返回内部字符串去掉首尾括号后的文本equals/hashCode契约与CalculationOperation相同equals要求other也是CalculationInterpolation且内部值相等。需要注意它的演进状态在当前 spec/js-api/value/calculation.d.ts.md 中CalculationInterpolation已被标记为废弃替代品——因为编译器现在能在求值时判断插值原本是否被括号包围所以不再需要它来传递该信息。编译器本身永远不会再返回CalculationInterpolation但在 JS API 做出破坏性修订之前用户仍然可以构造它并传给编译器以保持向后兼容例如显式强制输出calc()表达式而不化简为数字。序列化时CalculationInterpolation总是原样输出其value在CalculationOperation中它会被括号包围见 spec/types/calculation.md 的序列化规则。Value.assertCalculation类型收窄守卫declare module ../spec/js-api/value { interface Value { assertCalculation(name?: string): SassCalculation; } }assertCalculation(name?: string)是Value家族断言方法的一员若this是SassCalculation则原样返回否则抛出错误。可选的name参数用于错误报告让报错信息带上调用方上下文。这一族方法还包括assertBoolean、assertColor、assertNumber、assertString、assertMap等见 spec/js-api/value/index.d.ts.md。典型用法是在自定义函数中把收到的参数收窄为计算类型const sass require(sass); // 假设从 options.functions 中注册styles 中调用 mixin-calc($x) const fn (args) { const calc args[0].assertCalculation(mixin-calc); // 非计算值则报错 return calc; // 返回计算本身 };Options.functions的规格修订计算值在自定义函数边界的化简草案对 spec/js-api/options.d.ts.md 中Options.functions的规范做了替换式修订这是整个提案中与运行时行为关系最密切的部分。修订后的流程如下编译开始前对 record 中的每一对signature/function若signature不是 后紧跟ArgumentDeclaration的形式抛错取name为signature的 若已存在一个名字与name下划线不敏感_与-等价相等的全局函数跳过该键值对否则注册一个签名为此signature的全局函数。当它被调用时调用关联的CustomFunction若抛错则视为 Sass 函数抛出的 Sass 错误若result是或传递性地包含以下内容抛错不是Value类实例的对象signature字段不是合法 Sass 函数签名即不能出现在样式表function之后的签名的SassFunction返回result.internal的一份副本其中所有传递性包含的计算包括返回值本身若是计算都被替换为化简后的结果。最后一步正是化简只发生在自定义函数边界这一设计决策的落点。它引用的 simplifying 算法 定义在语言侧规范中要点包括calc(...)若化简为单个数字或计算则直接返回该值sin/cos/tan等单参数数学函数若参数为单个数字直接调用sass:math对应函数求值min/max/clamp在所有参数为数字且单位兼容时调用math.min()/math.max()/math.clamp()求值单位确定不兼容则抛错/-运算在两侧为兼容单位数字时折叠为数字*//在两侧为数字时直接做乘法或math.div其余情况保留为计算CalculationOperation/计算结构交给浏览器解析。因此JS 侧构造的SassCalculation.calc(sass.number(1, px).plus(sass.number(10, px)))一旦作为自定义函数返回值进入 Sass就会在边界处化简为数字11px而calc(1px 10%)因单位不可在编译期相加仍以calc(1px 10%)形式输出。顺带一提语言侧 First-Classcalc()提案 还说明了此类化简的刻意边界规范不做诸如calc(1px var(--length) 1px) → calc(2px var(--length))的激进最小化这类高级化简留给专门的 CSS 压缩后处理工具。实战在自定义函数中构建与返回计算值结合 js-api-doc/value/calculation.d.ts 中的类型定义一个完整的、可运行的思路如下以同步编译为例const sass require(sass); const result sass.compileString( use sass:math; .box { width: fluid-gap(16px, 48px); height: fit(100px, 200px, 50vh); } , { functions: { // 返回 calc(min (max - min) * var(--ratio)) fluid-gap($min, $max): (args) { const min args[0].assertNumber(fluid-gap); const max args[1].assertNumber(fluid-gap); const range new sass.SassCalculation.calc( new sass.CalculationOperation(-, max, min) ); return sass.SassCalculation.calc( new sass.CalculationOperation( , min, new sass.CalculationOperation( *, range, new sass.SassString(var(--ratio), {quotes: false}) ) ) ); }, // 返回 clamp(min, value, max)value 省略时依赖 var() 兜底 fit($min, $max, $value): (args) { return sass.SassCalculation.clamp( args[0], args[1], new sass.SassString(var(--fallback), {quotes: false}) ); } } });要点回顾构造CalculationOperation时左右操作数都不得是带引号字符串SassString若不希望触发抛错需用quotes: false创建未加引号字符串返回值若是计算会在自定义函数边界被化简——fluid-gap中max - min若可折叠会先折叠最终整体若可化简为数字则返回数字assertCalculation(name)用于把收到的任意Value收窄为SassCalculation失败时抛出带名字的错误。类型出口与集成位置SassCalculation、CalculationValue、CalculationOperator、CalculationOperation、CalculationInterpolation都从 value 模块统一导出集成方可以直接从sass包的 value 入口导入见 spec/js-api/value/index.d.ts.md 的导出清单其中CalculationValue与CalculationOperator两个纯类型自 Draft 3 起正式导出。所有相关类都带有category Custom Function标注明确它们的主要消费场景是自定义函数。版本演进Draft 1 → Draft 3.1变更记录 完整还原了草案的演进脉络版本关键变更Draft 1初始草案Draft 2化简时机从构造时急切化简改为从 JS API 返回时化简——即确立了本文所述的自定义函数边界化简模型Draft 3CalculationOperation与CalculationInterpolation由抽象类改为具体类导出CalculationValue与CalculationOperator类型调整clamp使逗号分隔的min可作为value/max的合法输入Draft 3.1收窄并澄清Calculation.clamp()多参数行为equals()参数类型改为unknown以匹配 immutable.js 类型把引号字符串的传递性检查下沉到CalculationOperation构造函数小结JavaScript Calculation API 为 Sass 的 JS 集成补上了计算类型的最后一块拼图语言侧有 计算类型规范 定义数据形态与化简算法JS 侧则以SassCalculation 两个ValueObject提供结构化构造与只读访问配合Value.assertCalculation做类型收窄并通过Options.functions的边界化简规则保证JS 侧可以自由构造未化简结构、Sass 侧负责最终化简。理解这套 API等于同时理解了 Sass 计算类型在语言与宿主两个世界之间的完整生命周期。赞分享前端【免费下载链接】sassSass makes CSS fun!项目地址https://gitcode.com/gh_mirrors/sa/sass点击查看免费下载相关推荐WinAsar重新定义Electron asar文件的可视化处理范式WinAsar重新定义Electron asar文件的可视化处理范式 在Electron应用开发领域asar文件格式作为资源打包的标准方案长期以来面临着命前端EdgyArc-fr主题定制教程打造独一无二的Firefox视觉体验EdgyArc fr主题定制教程打造独一无二的Firefox视觉体验 EdgyArc fr是一款基于Firefox Userchrome和Sidebery风格CSS 函数实战指南用 calc()、min()、max() 与 clamp() 构建响应式布局curriculum 中级 CSS 课程CSS 函数实战指南用 calc 、min 、max 与 clamp 构建响应式布局curriculum 中级 CSS 课程 本篇文章源自 curricu文档教程教育创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询