借用网上一个段子开头
面试官灵魂拷问:你是如何优雅的管理css的?
答:先写类名,在写样式内容
卒,享年18
Tailwind CSS 能帮你
借助tailwindcss官网的定义:
- Tailwind CSS 是一个功能类优先的 CSS 框架,它集成了诸如 flex, pt-4, text-center 和 rotate-90 这样的的类,它们能直接在脚本标记语言中组合起来,构建出任何设计。
- 无需离开您的HTML,即可快速建立现代网站
先来说说,css的进化历史
由于原生css存在许多痛点
- 全局污染
- 命名混乱
- 难以复用
- 维护性差
- 冗余代码
- 兼容问题
在 CSS 的进化历史上,出现过各种各样的框架或者手段致力于解决以上的问题
SASS, LESS,Stylus 等 CSS 预处理器
优点
- 提供了变量,函数,运算,继承等。扩展性,复用性都有了很大的提升
- 支持选择器嵌套
- 解决了一些样式重用冗余的问题
不足
- 对于命名混乱和全局污染问题的效果不大
- 嵌套层级过深则不容易阅读
BEM (.block__element–modifier) 规范
优点
- 比较流行的 class 命名规则,部分解决了命名混乱的问题
不足
- className 定义起来冗长
- 命名压力大,容易现很多无效的命名
CSS Modules
优点
- 模块化 CSS,将依赖的 CSS 文件以模块的形式注入到 JavaScript 里,基本上解决了全局污染、命名混乱、样式重用和冗余的问题
不足
- 与组件库难以配合,需要使用:global
- 会带来一些使用成本,本地样式父子组件覆盖困难,需要山路18弯
- 骆驼式命名的写法很蛋疼
- 丢失了 css 的灵活性
CSS in JS
// style-components
const Button = styled.a`
/* This renders the buttons above... Edit me! */
display: inline-block;
border-radius: 3px;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
width: 11rem;
background: transparent;
color: white;
border: 2px solid white;
/* The GitHub button is a primary button
* edit this to target it specifically! */
${props => props.primary && css`
background: white;
color: black;
`}
`
render(
<div>
<Button
href="https://github.com/styled-components/styled-components"
target="_blank"
rel="noopener"
primary
>
GitHub
</Button>
<Button as={Link} href="/docs">
Documentation
</Button>
</div>
)
const Button = styled.a`
/* This renders the buttons above... Edit me! */
display: inline-block;
border-radius: 3px;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
width: 11rem;
background: transparent;
color: white;
border: 2px solid white;
/* The GitHub button is a primary button
* edit this to target it specifically! */
${props => props.primary && css`
background: white;
color: black;
`}
`
render(
<div>
<Button
href="https://github.com/styled-components/styled-components"
target="_blank"
rel="noopener"
primary
>
GitHub
</Button>
<Button as={Link} href="/docs">
Documentation
</Button>
</div>
)
优点
- 组件化,方便开发维护和测试,JavaScript 和 CSS 可以方便的共享变量和方法
- 基于状态的样式定义,非常适合需要动态样式的组件开发
- 保留了原生css的写法,同时支持嵌套,js变量等
- 可以跨平台
不足
- ide支持较差
- 陡峭的学习曲线
- 降低代码可读性
- 无法提取静态css文件
- 运行时性能损耗
以上几种css技术解决方案没有好坏之分
,不同的技术方案适用于不同的应用场景,大家在选型的时候需结合实际使用场景
前端css颗粒度划分
<div style="{ borderRadius: '0.5rem', padding: '1rem' }">Click</div>
<div class="rounded-lg p-4">Click</div>
<div class="button">Click</div>
<Button>Click</Button>
搬运知乎回答 山月
越往下走,颗粒度越来越大,约束性变高,自由性不足。而 tailwindcss
位于第二层
核型理念
tailwindcss总共分为三大块
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind base;
基础(或全局)样式,可为诸如<a>
标签、标题等基本 HTML 元素设置有用的默认值,或者有目的重置,像流行的 box-sizing reset
如果你的项目中已经引入了默认样式,例如normalize.css
,那么这部分基础样式包可以选择不引入,或者通过tailwind.config.js
中配置preflight: false
来关闭
// tailwind.config.js
module.exports = {
// ... other config
corePlugins: {
preflight: false
},
}
扩充你的基础类
@layer base {
@font-face {
font-family: NumberFont;
src: url(https://hgkcdn.oss-cn-shanghai.aliyuncs.com/test/NumberMedium.2736700f.ttf);
}
@variants hover, focus {
.font-number {
font-family: NumberFont;
}
}
}
@tailwind components;
tailwind定义的一些组件类,目前只有一个container类,与bootstrap的container类作用一样。
扩充你的组件类
@layer components {
.btn-blue {
@apply py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75;
}
}
@tailwind utilities;
这个是tailwind的核心,引入了这个tailwindcss才会去生成对应的属性类。
扩充你的功能类
@layer utilities {
.scroll-snap-none {
scroll-snap-type: none;
}
.scroll-snap-x {
scroll-snap-type: x;
}
.scroll-snap-y {
scroll-snap-type: y;
}
}
tailwind 的工具类
涵盖了大部分的常规需求,官网上有详细的列表:
- 布局
- 弹性布局
- 网格布局
- 盒对齐
- 间距
- 尺寸
- 排版
- 背景
- 边框
- 特效
- 表哥
- 过渡和动画
- 转换
- 交互
- svg
- 可访问性
1. 原子化 CSS (Atomic CSS)
TailwindCss 将类名拆到了最小的单位,我们只需要用到一定数量的原子类,就能完成一个复杂的页面样式。这也是为啥使用 TailwindCss 的 tree-shake 后,压缩后的 css 体积可以降低到 10kb 以下的原因。
<!-- Using utilities -->
<button class="py-2 px-4 font-semibold rounded-lg shadow-md text-white bg-green-500 hover:bg-green-700">
Click me
</button>
<!-- Extracting classes using @apply -->
<button class="btn btn-green">
Button
</button>
<style>
.btn {
@apply py-2 px-4 font-semibold rounded-lg shadow-md;
}
.btn-green {
@apply text-white bg-green-500 hover:bg-green-700;
}
</style>
2. 响应式设计
TailwindCss 中的每个功能类都可以有条件的应用于不同的断点(breakpoints),在不同分辨率设备上,可以轻松切换属性。内联样式中,无法使用媒体查询。
根据常用的设备分辨率方案,默认内置了 5 个断点:
要添加一个仅在特定断点生效的功能类,只需要在该功能类前加上断点名称,后面跟 : 字符。
<!-- Width of 16 by default, 32 on medium screens, and 48 on large screens -->
<img class="w-16 md:w-32 lg:w-48" src="...">
3. 伪类
与 TailwindCss 处理 响应式设计 类似,通过为功能类添加适当的状态变体前缀,可以对处于 hover 、focus 和其它状态的元素设置样式, 而内联样式无法设置 hover 或者 focus 这样的状态。
<button class="bg-red-500 hover:bg-red-700 ...">
Hover me
</button>
4. 函数与指令
@tailwind
指令将 Tailwind 的预设基础样式导入到项目的样式表中@apply
指令将行内存在的(共用)基础类提取出来放到项目的 CSS 样式表中,「汇总」为一个类,然后在 HTML 元素就可以只写这个类名就应用其包含的所有基础类@layer
指令告诉 Tailwind 自定义样式属于哪个 bucket,通过该指令可以直接在项目的样式表 styles.css 定制基础类、伪类变量等,但还是推荐在配置文件 tailwind.config.js 中进行设置@variants
指令可以在样式表中使用或定制伪类变量@responsive
指令可以在样式表中使用断点@screen
指令可以在样式表中使用屏幕相关的媒体查询,设置响应式样式theme()
函数可以在样式表中读取 Tailwind 配置文件的值
原理
没有任何黑魔法,仅仅是排列组合而已 (no magic just permutationCombination)
/* Input */
@variants hover, focus {
.banana {
color: yellow;
}
}
/* Output */
.banana {
color: yellow;
}
.hover\:banana:hover {
color: yellow;
}
.focus\:banana:focus {
color: yellow;
}
例如声明一个banana
,并且应用hover
与focus
伪类,那么通过@variants
并追加hover, focus
,tailwind编译器便会通过排列组合生成响应的css
。
说实话在未使用tailwindcss之前也通过less
、scss
干过类似的事情,例如通过less的函数循环生成对应的类名
// input
.genGrid(@n, @i: 1) when (@i =< @n) {
.grid@{i} {
width: (@i * 100% / @n);
}
.genGrid(@n, (@i + 1));
}
.genGrid(5);
// output
.grid1 {
width: 20%;
}
.grid2 {
width: 40%;
}
.grid3 {
width: 60%;
}
.grid4 {
width: 80%;
}
.grid5 {
width: 100%;
}
性能优化
tailwindcss的原理是排列组合,所以组合越多,最终生成的css文件越大,使用默认配置,TailwindCSS 的开发版本是3739.4kB未压缩,293.9kB用Gzip进行压缩,73.2kB用Brotli进行压缩。,所以我们需要结合主流构建工具,例如webpack,来剔除我们未使用到的类及其内容。
配置十分简单
// tailwind.config.js
module.exports = {
// ...otherConfig
purge: [
'./src/**/*.html',
'./src/**/*.vue',
'./src/**/*.jsx',
],
}
其原理就是通过正则表达式扫描指定的文件列表,将未使用到的类剔除,效果类似tree shaking
所以在写类名的时候最好写全
❌ bad
<div class="text-{{ error ? 'red' : 'green' }}-600"></div>
✅ good
<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>
安装配置
npm install tailwindcss@latest postcss@latest autoprefixer@latest
注意需要PostCSS 8且Node版本 > 12.13
如果是postcss7,那么执行一下两个命令,安装postcss&兼容版本
npm uninstall tailwindcss postcss autoprefixer
npm install -D tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
创建一个 tailwind.css
文件
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
通过指令创建tailwind.config.js
npx tailwindcss init
// tailwind.config.js
module.exports = {
purge: [
'./src/**/*.html',
'./src/**/*.vue',
'./src/**/*.jsx',
],
theme: {},
variants: {},
plugins: [],
}
配置postcss.config.js
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
默认情况下tailwindcss的间距都是使用rem
为标准的,你也可以自定义theme来将其改成px
,以下是我在项目中使用的配置
module.exports = {
// ... other config
theme: {
fontSize: {
xs: '12px',
sm: '14px',
base: '16px',
lg: '18px',
xl: '20px',
'2xl': '24px',
'3xl': '30px',
'4xl': '36px',
'5xl': '48px',
'6xl': '60px',
'7xl': '72px',
},
spacing: {
px: '1px',
0: '0',
0.5: '2px',
1: '4px',
1.5: '6px',
2: '8px',
2.5: '10px',
3: '12px',
3.5: '14px',
4: '16px',
5: '20px',
6: '24px',
7: '28px',
8: '32px',
9: '36px',
10: '40px',
11: '44px',
12: '48px',
14: '56px',
16: '64px',
20: '80px',
24: '96px',
28: '112px',
32: '128px',
36: '144px',
40: '160px',
44: '176px',
48: '192px',
52: '208px',
56: '224px',
60: '240px',
64: '256px',
72: '288px',
80: '320px',
96: '384px',
},
extend: {
lineHeight: {
3: '12px',
4: '16px',
5: '20px',
6: '24px',
7: '28px',
8: '32px',
9: '36px',
10: '40px',
},
},
},
}
tailwind v1.x 升级 v2.x注意点
- postcss版本需要升到
8
,或者安装兼容版本tailwindcss@npm:@tailwindcss/postcss7-compat
- Node版本需要升到
12.13.0
以上 - 如果升级了
postcss
注意升级对应的postcss 插件包
vs code编辑器插件
支持智能提示,语法高亮,鼠标hover显示具体类内容等,如果发现不生效,请升级vs code编辑器版本
实用工具
配置中可能存在的问题
1. css
、less
、scss
样式文件中stylelint
提示unknownAtRules
,
// .stylelintrc.js
{
"rules": {
'at-rule-no-unknown': [
true,
{
ignoreAtRules: ['tailwind', 'apply', 'variants', 'responsive', 'screen'],
},
],
}
}
2. Vue项目中,sfc文件,在vscode中提示 Unknown at rule @apply scss(unknownAtRules)
警告
// setting.json
{
"css.validate": false,
"less.validate": false,
"scss.validate": false,
"css.lint.unknownAtRules": "ignore",
}
优势
- 提高了开发效率,对于一个简单的样式需求,例如间距,不用在自己定义一个
class
,直接使用mt-1
这样即可 - 不用在为
给类命名
而浪费精力 - CSS 体积不会因为项目的迭代而增长
更改会更安全
,不用担心css全局性带来的样式混乱- 自带专业的ui设计风格,不会写出花花绿绿的页面,整体视觉效果统一,包括间距、色彩、字体等等
不足
- class名称过长,影响阅读
真香!!!
使用了
tailwindcss
并不意味这你所有样式都需要写在html里,你也可以通过BEM
规范命名元素,并通过@apply
组合样式,当然你也可以自己扩展tailwindcss
,提高复用性。
总结
起码你再也不用为命名
烦恼,对于简单一些css样式,也不再需要命名一个class
或者写到style
里
虽然但是,所有的技术方案都需要结合实际使用场景,否则将变得毫无意义,例如
css in js
和css module
方案,在开发一些公共组件库的时候并不是那么合适,这个时候BEM
+sass/less
或许更加合适,因为前者为解决css作用域问题都会给class加上一个随机字符串,这对用户想要自定义样式等操作来说并不友好。
参考文章