15
头图

CSS 是声明式语言,很简单,很好学,但是写起来很累,所有东西都要写出来才能生效。复用方面更是无从下手,虽然大家都在不断总结,但始终没能找到足够好用的方案,可以有效改善 CSS 开发。

于是我们只好把视线转出 CSS 之外,转投 CSS 预处理工具,Less、SASS(SCSS)、Stylus,引入种种 CSS 不具备的功能,帮助我们改进开发体验。比如嵌套、函数、循环、条件,等等。然而如果你细心观察,实际上,这几个工具最近 5、6 年都没怎么更新(我说的是功能性),因为该有的都有了,甚至很稳定;其它来自于 CSS 的改进,几乎跟它们没什么关系,也不用更新。

最近几年,随着 CSS 发展,一些新特性逐步引入,我觉得这些工具越来越难用,它们能带来的好处已经无法掩盖它们所造成的问题。是时候告别 CSS 预处理工具了,就像我们当年告别 jQuery 一样。

为什么说预处理工具落后?

我把理由分成三大类:

预处理工具的问题

  • 对 CSS 函数兼容性不好,尤其是 rgba()hsl() 这些常用的颜色函数
  • 数值类型转换,有不符合预期的行为,比如 Stylus 实现 content:5

CSS 的改进

  • CSS 拥有越来越多的函数,可以直接进行计算,比如前面提到的颜色;还有 calc() 来完成基础数学计算
  • CSS 变量非常好用,可以大大改进编程体验,配合各种 JS 框架,我们可以更容易的把数学逻辑和显示效果绑定在一起
  • CSS Houdini 可以实现很多新功能,即使不深入使用(JS 部分),也有好用的自定义属性
  • CSS 也开始从预处理工具吸收营养,比如近期的嵌套功能已经开始被整合,未来我们可以直接使用

预处理工具无法跟进的问题

  • 很多缩写、复合属性无法处理,比如 background-image、box-shadow 等,都支持多属性共同生效,预处理工具擅长的循环、条件、函数无法提供帮助。
  • 预处理,顾名思义,发生在生产之前。实际上,网页在实际浏览时,会有很多因素影响到渲染结果,比如分辨率、dark mode 等。预处理工具对这些需求也没有改进。

替代方案

我目前的替代方案基于 TailwindCSS,所以自然包含 PostCSS、AutoPrefixer 等工具。然后用 postcss-import 实现自动导入和模块化;使用 tailwindcss/nesting 实现嵌套。

为什么选用 TailwindCSS?首先,实际开发中,不管使用什么前端框架,我们都需要大量原子化的胶水样式,比如调整间距、改变字号、给容器添加一些边框、圆角、阴影等。这些样式如果都手写,工作量并不小;学习不同的样式名也是负担;以及最重要的,CSS 优先级问题。使用 TailwindCSS 就都能很好解决。

TailwindCSS 不仅包含一大堆原子化样式,自身也是个完整且优秀的 CSS 编译器。它包含 reset,提供一组全局通用的 CSS 变量;它可以从各种文件里把我们用到的样式提取出来,构建后生成的 CSS 里只有我们要用到样式,不会有多余的;它会分析我们对样式的使用,合理的调整样式顺序,保证样式能正确生效。使用 TailwindCSS 可以节省很多时间。

它还自带若干插件,比如解决嵌套的 tailwindcss/nesting,支持内容类元素的的 @tailwindcss/typography 等。使用这些插件也可以帮我们节省很多时间。

最后,TailwindCSS 的生态不断成长,我们的选择范围越来越宽:HeadlessUI、DaisyUI、付费的 Tailwind UI 等。方便我们从产品生命周期的任意阶段开始集成。

推荐项目配置

启动项目的时候,安装依赖。包含 PostCSS + AutoPrefixer、TailwindCSS 和 DaisyUI。前者提供 CSS 处理框架,包含自动导入 css 和嵌套功能;后两者提供可见的 UI。

pnpm i postcss postcss-import tailwindcss autoprefixer daisyui -D

自动初始化配置,-p 会自动生成 PostCSS 配置:

pnpm tailwindcss init -p

调整 postcss.config.js,启用 postcss-importtailwindcss/nesting。目前我们常用的嵌套规则和 CSS 规范略有区别,不过无所谓,规范也没确定,所以这样就足够了。

// postcss.config.js
module.exports = {
  plugins: {
    'postcss-import': {},
    'tailwindcss/nesting': {},
    tailwindcss: {},
    autoprefixer: {},
  },
}

然后调整 tailwind.config.js

// tailwind.config.js
const DaisyUI = require('daisyui');
// 这个插件可以帮我们处理文档类内容,我建议常用
const Typography = require('@tailwindcss/typography');

module.exports = {
  // 从以下文件查找用到的样式
  content: [
    './index.html',
    './src/**/*.{js,ts,jsx,tsx,vue}',
  ],
  theme: {
    extend: {
      // 扩充 TailwindCSS 没有包含的样式
    },
  },
  plugins: [
    DaisyUI,
    Typography,
  ],
  daisyui: {
    themes: [{
      // 只构建一个主题: luxury,并覆盖其中的两个属性
      luxury: {
        ...require('daisyui/src/colors/themes')[ '[data-theme=luxury]' ],
        primary: '#FFA028',
        '--bc': '0 0% 87.5%',
      },
    }],
  },
}

然后,创建样式入口 main.css。其它样式可以如常写在这个文件里,不过如果要 @import 其它 CSS 文件,就要进行一些调整。具体可以看官方文档。

// main.css
@tailwind base;
@tailwind components;
@tailwind utilities;

然后在入口文件引用 main.css 即可:

// main.js
import './main.css';

至此,新项目配置完成,可以照常开发了。

下期预告

这次我先分享了整体思路:用新的工具链替代预处理工具,保证已有的功能不缺失。那么下期分享的内容就是使用新的 CSS 特性,更好的完成开发。


如果你对新 CSS 感兴趣,对预处理工具和新工具链有兴趣和疑问,欢迎留言讨论。如果本文对你有启发,也请帮我点赞分享,谢谢。


本文参与了SegmentFault 思否写作挑战赛,欢迎正在阅读的你也加入。

Meathill
22.3k 声望8.6k 粉丝

爱编程,爱旅游,爱吐槽。