最近在修改一个项目的时候,发现了一系列的 Sass 的告警——由除号引起的告警:
目录
什么告警?
告警的内容很简单,用 /
作为除法已经在 Dart Sass 2.0.0 中被弃用了,作为一个 Sass 的基础语法,这次弃用属于 breaking change 了,因此目前编译时只是会抛出 warning 而不是 error,否则大量项目都无法正常运行。
研究了一下可以看到,Sass 官方特定用了一整个篇幅的文章,来阐述为何要作出这个修改,主要的原因在于,/
在 Sass 中同时承担除号以及 CSS 分隔符的作用,例如:
Sass 代码
// 作为除号使用
.test_division {
border-radius: ceil(28px / 2);
}
// 作为 CSS 分隔符使用
.test_operator {
font: 12px/1.5 -apple-system, "SF UI Text", "PingFang SC", "Lucida Grande", "Microsoft YaHei", sans-serif;
}
实际编译出的 CSS 代码
.test_division {
border-radius: 14px;
}
.test_operator {
font: 8px -apple-system, "SF UI Text", "PingFang SC", "Lucida Grande", "Microsoft YaHei", sans-serif;
}
示例中有两个 /
,一个是用作除号,另一个是作为 font
属性中 font-size
和 line-height
的分隔符,可以看到作为除号使用的时候,/
一般不会出现什么问题,但是作为分隔符使用的时候,/
很容易被重载为除号,实际上要实现分隔符的效果,通常需要这样编写:
.test_operator {
font: #{12px/1.5} -apple-system, "SF UI Text", "PingFang SC", "Lucida Grande", "Microsoft YaHei", sans-serif;
}
使用了插值(Interpolation)语法,包裹了 12px/1.5
,插值的作用是仅解析 Sassscript,把 Sass 变量输出为实际的值,但不会进行运算,如果属性值比较复杂,则会导致编写的时候不大直观。
Sass 本身使用了 complex heuristics 的技术去判断 /
应该作为除号还是分隔符,complex heuristics 是需要回顾当前上下文的内容,来作出判定的,因此对于 Sass 来说存在一定消耗。
综合来说,原有的 /
语法对于开发者会带来一些困扰,尤其是随着 CSS 有更多的属性使用到了分隔符(例如 grid
、hsl()
等语法),同时对于 Sass 的维护以及编译也会带来一些额外的消耗,所以 Sass 最终决定重新定义除法,新的语法也相当清晰:
@use "sass:math";
.test_division {
border-radius: math.div(28px, 2);
}
新的语法基于 Sass 的 module 语法,引入了相关的运算模块后,就可以调用 math.div
代替原来的 /
,而 /
则只作为分隔符使用。
如何解决告警?
要解决告警,首先要知道为何项目中会出现这个告警,用到了这个语法的项目比较多,但目前只有这个项目出现了告警。
首先这个 breaking change 仅在 Dart Sass 的最新版本中才引入,Node Sass 的版本目前还没有跟上。另外该项目中是使用 "sass": "^1.30.0"
来声明 sass 模块的版本(即 Dart Sass 的 npm 包),重新执行 npm i
会导致安装上新版的 Dart Sass 从而出现告警,因此解决方案也围绕 Dart Sass 版本去处理。
降级 sass 模块
删除 package-lock.json
与 node_modules
,把 package.json
中 sass 模块的版本改为 "sass": "~1.32.12"
,即版本号会少于 1.33.0
,这个版本的 Dart Sass 并未引入 slash as division 的 breaking change。
更新业务语法
即按上面提到的方式,把相关的告警内容改为用新的 math.div
语法代替,如果涉及的业务量比较大,更新起来会比较费时。
使用 sass-migrator
sass-migrator 是 Sass 官方推出的迁移工具,方便开发者对原有的业务代码进行最新版本的 Sass 适配。sass-migrator 并不是把相关的旧语法直接替换为最新的语法,而且采用更稳固的方式,把代码安全地更新为符合最新要求的语法,例如上面的例子:
Sass 代码
.test_division {
border-radius: ceil(28px / 2);
}
sass-migrator 的安装与调用
npm i -g sass-migrator
sass-migrator division test.scss
处理后的 Sass 代码
.test_division {
border-radius: ceil(28px * 0.5);
}
可以看到,sass-migrator 并没有把原有的 /
修改为最新的 math.div
语法,而是修改为用乘法代替。实际上跟 sass 基于 complex heuristics 进行分析会把分隔符重载为除号一样,迁移工具也无法准确地判断每个 /
的作用,因此 sass-migrator 采用了稳固的方式去适配最新的 Sass 规则。
Dart Sass Vs Node Sass
Node Sass 由于没有引入最新的 Sass 特性,因此并不会出现这个告警,但并不建议使用 Node Sass 代替已经用 Dart Sass 编写的代码,主要是:
- Dart Sass 已经是官方的首选,无论是新特性还是问题修复,Dart Sass 会有更强的时效性,担心新特性会为业务带来问题可以通过锁包进行控制。
- Node Sass 实际上已经被官方定义为 deprecated,目前项目会维护一个主要版本,但是维护进度并不确定,并且明确没有计划再为 Node Sass 添加新特性,也不会适配 CSS 的新特性。
- Node Sass 基于 LibSass 开发,而 LibSass 依赖了的模块安装比较麻烦,尤其是对于 Windows 的用户,它要求用户在 Windows 中必须安装 Python2 和 Visual Studio。
因此出于长期维护的考虑,选用 Dart Sass 也是一个趋势。
Dart Sass on Dart-VM 与 Dart Sass on NPM
目前 Dart Sass 有两种实现,分别是:
- 基于 Dart-VM 的 Dart Sass
- 基于纯 JavaScript 的 Dart Sass
根据官方的介绍,单独运行的命令行版本,是基于 Dart-VM 运行的,得益于 Dart-VM 的高性能,这个版本的 Dart Sass 性能非常好,适合用于编写脚本单独编译 Sass 文件。
而 NPM 中的 Dart Sass 则是纯 JavaScript 实现,因此可以很方便地用于前端项目构建。虽然 JavaScript 版本的 Dart Sass 性能比 LibSass 要差一些,但是对于样式代码的编译量来说,区别并不大。
上图是 Dart Sass on Dart-VM、Dart Sass on NPM、Node Sass 分别去编译 BootStrap 4 的耗时(来源于 Stack Overflow),可以看出 Dart Sass on NPM(即 Dart Sass JS)比 Node Sass 还要慢很多(大概3倍的耗时),但实际上即使是 Bootstrap 这个体量,也只是2秒的耗时,考虑到官方和社区都逐步迁移到 Dart Sass 了,因此新项目也建议使用 Dart Sass。
本文由 Kayo Lee 发表,本文链接:https://kayosite.com/analysis-of-sass-breaking-change-slash-as-division.html
评论列表