#前言
本文写于 TailwindCSS V4 + Vite + Vue3 环境
我之所以使用 TailwindCSS 编写组件又使用 @apply 将样式写在 css 文件而不是写在 class 属性内原因有两点
- 方便覆盖,如果写在
class属性内只能通过引入tailwind-merge来动态解析哪些冲突,这是运行时的行为,感觉会多出一比无意义的性能开销 - 解决 class 太长问题,在组件中写 tailwindcss 常见的就是 class 名太长太多了,非常混乱还超过了 printwidth 80,阅读及其难受
为什么不放弃使用 tailwindcss
懒,能少写一点是一点,顺便用 tailwindcss 定义好的颜色组等
Vue 支持内联 CSS 因此组件的样式基本写在 <style> 标签内
#样式覆盖问题
组件定义样式后,用户使用 TailwindCSS 样式自定义组件某些样式时会无法覆盖,因为其优先级会低于组件样式的优先级。而使用 TailwindCSS 时除了使用 important 或写 CSS 文件基本无法解决,因此需要编写组件时降低样式的优先级
降低优先级的解决方案:
#CSS where 选择器
使用 where 选择器 (MDN 文档) 可以始终将优先级设置为 0,比如
:where(.ui-button) {
@apply inline-flex;
} 非常合适用于避免和用户自定义样式冲突
#CSS layer 特性
layer 是 css 的一个实验性特性 (MDN 文档)。
其作用用于组织样式到不同的层中,layer 会按照声明定义顺序,后声明的 layer 优先级会高于前面的。
使用 Tailwind 基本都会用到 layer, Tailwind 中默认定义了 4 种 Layer,分别是 theme、base、components、utilities
因此可以使用 components layer 来定义组件样式
@layer components{
:where(.ui-button) {
@apply inline-flex;
}
} 这样就能确保该组件不会和用户自定义样式冲突,就算没有 where 选择器也不会和 utilities 层的例如 px-2 等起冲突。
使用 layer 就必需确保 layer 顺序正确,然而很不幸被打乱了
#layer 顺序被打乱问题
TailwindCSS 所使用的 Layer 顺序可以在 tailwindcss/index.css 第一行看到
@layer theme, base, components, utilities; 因此顺序便是
theme -> base -> components -> utilities 像常见的 p-2、flex 等都是定义在 utilities layer 中的,优先级是这几个 layer 中最高的
在 Vue 中使用 style 标签默认会在组件挂载时将样式注入到 <head> 末尾,这就可能导致组件样式会比 global.css 提前注入到 head 中。
比如 toast 这种常见于全局挂载的组件,当这种组件中使用了 components layer 就会导致 layer 顺序变成了
components -> theme -> base -> utilities 这就会导致某些样式比如 tailwindcss 中使用 base 层的 prefight 覆盖了组件样式,如上示例可以看到打乱后的 base 层比 components 层高。
为什么会出现这种情况,其实是加载顺序的问题,通常导入 CSS 语句写在导入末尾的,例如
import { createApp } from 'vue';
import App from './pages/app.vue';
import "./global.css"
createApp(App).mount('#app'); App 组件会先于 global.css 加载,而在 App 中使用的全局组件则会先一步被注入到 <head> 标签内
为了解决该问题,只能通过确保 global.css 优先加载比如将其置顶
import "./global.css";
import { createApp } from 'vue';
... 或者将 layer 声明内联定义在入口 HTML head 标签内
<head>
<style>
@layer base, components, utilities;
</style>
</head> 也可以在入口 HTML 直接引入 global.css 文件
<head>
<link rel="stylesheet" type="text/css" href="/src/global.css"/>
</head>