引言
团队很多项目,无论是vite还是webpack,写样式时都用了sass + postcss,然而随着项目越来越重,打包耗时也越来越长,ts和js代码可以使用swc等rust工具来编译,替换后确实提速效果是比较明显的,然而css打包也是影响整体打包速度的重要因素,而找了半天没发现太好的适合我们场景的css打包工具,所以决定自己写一个。
分析需求
首先我们90%的项目都用了postcss,依赖于一些postcss的插件,社区的lightningcss相当优秀,但是没法作为postcss插件使用,所以需要自己封装。
其实已经有大佬基于lightningcss提供Node版本的封装了postcss-lightningcss
插件,但是遗憾的是该插件只有核心的编译功能是使用官方提供的用Rust开发封装过后的lightningcss
模块,其他写 postcss 插件的逻辑还是使用js实现的,甚至还引入了node模块 browserslist
来处理浏览器范围字符串的转化逻辑,所以性能上还是打了一些折扣的。这就让强迫症的我很是难受,要搞就搞个完美点的嘛。
@gogors/postcss-lightningcss-rs
那么 @gogors/postcss-lightningcss-rs 即是从头到脚完全用Rust实现的postcss插件,包括浏览器范围字符串的转化逻辑也是用的Rust模块,例如你可以写[">= .25%", "chrome 90"]
来描述兼容性,插件内部去帮你在处理具体文件前进行转化,只转化一次,而不是处理每个文件时都去转化一次。并将能缓存的地方缓存,只取我们需要的功能,相比于基于全量的node版本的lightningcss去封装,纯Rust版本的无论是逻辑还是体积都更轻量,但是功能上却是完全一致的。
最重要的是,该插件支持处理嵌套css语法,意味着我们不再需要使用sass,尤其是你只依赖sass的嵌套语法的情况,就完全没有必要使用sass了。
使用原生css去写嵌套语法,使用postcss + @gogors/postcss-lightningcss-rs 去编译压缩,可以完全抛弃sass,获得极致的打包速度提升,要知道使用sass本身编译为css也是有成本的。
个人认为原生 CSS 已经支持变量、嵌套规则和计算功能,结合postcss来处理兼容性问题以及模块化导入,在日常开发需求中已经足够,Sass 提供了更丰富的编程特性,包括混合(Mixins)、继承、函数、控制指令(如条件语句和循环)等,虽然很强大,但是使用这些功能的同时也引入了复杂性和维护成本,代码可读性也会明显下降,同时还增加了构建编译成本,得不偿失。
所以如果你只需要嵌套功能的话,从此以后可以抛弃sass了,再也不用处理sass环境问题带来的各种坑了。
想当初,我还因此专门写了一篇又报gyp ERR!为什么有那么多人被node-sass 坑过?的文章,sass真是把不少人折磨够呛。
安装
bash
yarn add -D @gogors/postcss-lightningcss-rs
使用方法
javascript
const postcss = require("postcss");
import { postcssLightningcssPlugin } from "@gogors/postcss-lightningcss-rs";
postcss([postcssLightningcssPlugin(/* pluginOptions */)]);
选项
javascript
import { postcssLightningcssPlugin, Features } from "@gogors/postcss-lightningcss-rs";
postcssLightningcss({
// 启用压缩(默认:true)
minify: true,
// 标记未使用的符号,会在压缩时移除
unusedSymbols: ['foo', 'fade-in', '--color'],
// 使用 browserslist 查询来指定支持哪些浏览器,以及要编译哪些特性
targets: [">= .25%", "chrome 90"],
// 即使目标支持,始终编译简化颜色和 CSS 嵌套。
include: Features.Colors | Features.Nesting,
// 即使目标不支持,也不要添加任何供应商前缀。
exclude: Features.VendorPrefixes
// true 启用所有文件的 CSS 模块,默认:false
cssModules: true,
// 单独启用各种css草案语法
drafts: {
// 启用自定义媒体查询(默认:false)
customMedia: false,
// 启用深选择器组合器(默认:false) 启用解析 Vue/Angular >>> 和 /deep/ 选择器运算符。
deepSelectorCombinator: false,
},
// 是否忽略无效的规则和声明,而不是报错。启用后,将返回警告,并将无效的规则或声明从输出代码中省略。
errorRecovery: false,
// 用 JavaScript 可以应用的类名替换用户操作伪类。
pseudoClasses: PseudoClasses,
});
Feature flags
include 和 exclude 选项允许明确开启或关闭某些功能。这些选项会覆盖基于提供的浏览器目标设置的默认值。例如,你可能只想编译颜色,并使用其他工具处理自动前缀或其他功能。或者,你可能想使用 Lightning CSS 处理除供应商前缀之外的所有内容。
include 和 exclude 选项使用 Features 枚举进行配置,可以从 lightningcss 导入。可以将多个标志进行位或操作以打开或关闭。
javascript
import { postcssLightningcssPlugin, Features } from '@gogors/postcss-lightningcss-rs';
let { code } = postcssLightningcssPlugin({
// ...
targets: [">= .25%", "chrome 90"],
include: Features.Colors | Features.Nesting,
exclude: Features.VendorPrefixes
});
unusedSymbols
如果知道某些类名、ID、 @keyframes 规则、CSS 变量或其他 CSS 标识符未使用,可以使用 unusedSymbols 选项来删除它们。
javascript
let { code } = postcssLightningcssPlugin({
// ...
minify: true,
unusedSymbols: ['foo', 'fade-in', '--color']
});
使用此配置,以下 CSS:
css
:root {
--color: red;
}
.foo {
color: var(--color);
}
@keyframes fade-in {
from { opacity: 0 }
to { opacity: 1 }
}
.bar {
color: green;
}
压缩为:
css
.bar{color:green}
Pseudo class replacement
Lightning CSS 支持用普通 CSS 类替换 CSS 伪类,如 :focus-visible ,这些类可以使用 JavaScript 操作。
javascript
let { code, map } = transform({
// ...
pseudoClasses: {
focusVisible: 'focus-visible'
}
});
以上配置将会替换所有选择器中的 :focus-visible 伪类为 .focus-visible 类。
以下伪类可以按照上述方式配置:
- hover -- 对应于 :hover 伪类
- active -- 对应于 :active 伪类
- focus -- 对应于 :focus 伪类
- focusVisible -- 对应于 :focus-visible 伪类
- focusWithin -- 对应于 :focus-within 伪类
CSS modules
启用 CSS 模块
javascript
postcssLightningcss({
cssModules: true,
});
还可以将其与自定义命名模式结合使用:
javascript
postcssLightningcss({
cssModules: {
pattern: "my-company-[name]-[hash]-[local]",
},
});
使用该插件后可以移除的 PostCSS 插件
PostCSS 插件 | lightningcss 选项 |
---|---|
autoprefixer |
取决于targets配置 |
postcss-clamp |
取决于targets配置 |
postcss-color-hex-alpha |
取决于targets配置 |
postcss-color-hsl |
取决于targets配置 |
postcss-color-rgb |
取决于targets配置 |
postcss-color-function |
取决于targets配置 |
postcss-color-rebeccapurple |
取决于targets配置 |
postcss-custom-media |
options.drafts.customMedia |
postcss-double-position-gradients |
取决于targets配置 |
postcss-hwb-function |
取决于targets配置 |
postcss-lab-function |
取决于targets配置 |
postcss-logical |
取决于targets配置 |
postcss-media-minmax |
取决于targets配置 |
postcss-multi-value-display |
取决于targets配置 |
postcss-nesting |
取决于targets配置 |
postcss-normalize-display-values |
取决于targets配置 |
postcss-oklab-function |
取决于targets配置 |
postcss-overflow-shorthand |
取决于targets配置 |
postcss-place |
取决于targets配置 |
postcss-progressive-custom-properties |
取决于targets配置 |
总结
使用纯血Rust版本的css编译压缩工具再加上去掉sass编译过程,打包速度简直起飞,小伙伴们快尝试起来。