我上次在群裡吐槽 Tailwind,被幾個大佬圍攻了:“現在還在寫傳統 CSS 的怕不是還在用 jQuery?”、“都 2025 年了還用 BEM?”整得我都不敢說話了。
作為一個前端搬磚工,我從 Nodejs 到 React 再到 Vue 都踩過一遍坑,今天就跟大家聊聊這個讓我又愛又恨的 Tailwind。
這是我第一次看到 Tailwind 代碼的反應:
<div class="flex flex-col md:flex-row items-center justify-between p-4 md:p-6 lg:p-8 bg-white dark:bg-gray-900 rounded-lg shadow-md hover:shadow-lg transition-shadow duration-300">
<!-- 還有一堆嵌套 div,每個都帶著幾十個類名 -->
</div>
同事問我:“這坨代碼意味著什麼?”我看了半天說:“一個卡片,會動,能響應式,深色模式適配了……”但我心裡想的是:這 TM 跟當年在 HTML 裡寫 style="color: red; font-size: 14px;" 有啥本質區別?
上個月接了個離職同事的專案,打開一看差點沒背過氣去:
<div className={`px-${size === 'large' ? 6 : size === 'small' ? 2 : 4} py-${hasIcon ? 3 : 2} ${variant === 'primary' ? 'bg-blue-500' : 'bg-gray-200'} ${isDisabled ? 'opacity-50 cursor-not-allowed' : 'hover:opacity-90'}`}>
{/* 還有 50 行類似的代碼 */}
</div>
這種動態拼接類名的操作,讓我調試的時候想砸鍵盤。查了半天發現有個按鈕在某種狀態下 padding 不對,原來是 px-${size} 這種騷操作導致的。
老闆跟我說:“用 Tailwind 開發速度快啊!”但真實情況是:
m-4 和 p-4 到底哪個是 margin 哪個是 padding?mt-4 和 mr-4 又是啥?shadow-lg 和 shadow-xl 的區別有這查文檔的時間,我 CSS 早寫完了。
兩個月後,當我對常用類名爛熟於心後,發現有些場景真香:
快速原型開發:產品經理站我身後:“這裡改個間距,那裡調個顏色,這個按鈕 hover 效果換一下……”
以前:切到 CSS 檔案 -> 找到對應的類 -> 修改 -> 切回來預覽 -> 重複
現在:直接在 HTML 裡改幾個類名 -> 實時預覽
設計一致性:以前團隊裡每個開發者對“大間距”的理解都不一樣,有人寫 margin: 20px,有人寫 margin: 24px,還有寫 margin: 1.5rem。現在統一用 m-5 或 m-6,UI 終於統一了。
我原來不信,直到對比了專案打包後的 CSS 檔案大小:
main.css 87KBmain.css 12KB因為 Tailwind 只生成你用到的樣式,不會有未使用的 CSS 代碼。
還記得那些年被 BEM 命名支配的恐懼嗎?
.card {}
.card__header {}
.card__header--active {}
.card__body {}
.card__footer {}
.card__footer__button {}
.card__footer__button--disabled {}
現在?直接寫樣式就行了,不用再想 header-wrapper-inner-content 這種傻逼名字了。
轉折點是我開始 用正確的方式寫 Tailwind。
function BadButton() {
return (
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
提交
</button>
);
}
function Button({
children,
variant = 'primary',
size = 'medium',
fullWidth = false
}) {
const baseClasses = "font-bold rounded transition-colors";
const variants = {
primary: "bg-blue-500 hover:bg-blue-700 text-white",
secondary: "bg-gray-200 hover:bg-gray-300 text-gray-800",
danger: "bg-red-500 hover:bg-red-700 text-white"
};
const sizes = {
small: "py-1 px-3 text-sm",
medium: "py-2 px-4",
large: "py-3 px-6 text-lg"
};
const widthClass = fullWidth ? "w-full" : "";
return (
<button className={`${baseClasses} ${variants[variant]} ${sizes[size]} ${widthClass}`}>
{children}
</button>
);
}
// 使用 cva 庫管理變體(更優雅)
import { cva } from 'class-variance-authority';
const buttonVariants = cva(
"font-bold rounded transition-colors", // 基礎樣式
{
variants: {
variant: {
primary: "bg-blue-500 hover:bg-blue-700 text-white",
secondary: "bg-gray-200 hover:bg-gray-300 text-gray-800",
},
size: {
small: "py-1 px-3 text-sm",
medium: "py-2 px-4",
}
},
defaultVariants: {
variant: "primary",
size: "medium"
}
}
);
// 實際使用
function GoodButton() {
return (
<Button variant="primary" size="large">
提交
</Button>
);
}
margin-top: 8px 而不是 10pxtailwind.config.js@apply 提取/* 在 CSS 文件中 */
.btn-primary {
@apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded;
}
clsx 處理條件類名,tailwind-merge 解決類名衝突用不用 Tailwind,其實跟你用什麼技術關係不大,關鍵看你怎麼用。
那些說 Tailwind 垃圾的,多半是看到了濫用它的專案;那些吹爆 Tailwind 的,多半是用對了方法。
就像當年大家吵 jQuery 和原生 JS,吵 React 和 Vue 一樣,最後你會發現:工具沒有對錯,只有適不適合。 牛逼的程序員用記事本都能寫出好代碼,菜雞用再牛逼的框架也能寫出屎山。
所以,別吵了,趕緊去寫代碼吧。老闆又改需求了,今天還得加班呢。
關注公眾號" 大前端歷險記",掌握更多前端開發乾貨姿勢!