免费体验 Gpt4 plus 与 AI作图神器,我们出的钱 体验地址:体验
使用 Tailwind CSS,我避免了在 React 项目中复制大量 CSS 文件的麻烦,使网页开发变得更加迅速高效。虽然 Tailwind 已经非常优秀,但我还是发现了一些技巧,可以进一步提升使用这个框架的体验。
IDE 设置
智能提示扩展
Tailwind 智能提示是一款适用于 VSCode 和 JetBrains(如 IDEA 和 Webstorm)的扩展。它提供自动补全功能,便于在 HTML 元素上编写类,并可访问 Tailwind 配置。若您忘记了定义的颜色名称,它还能帮助您快速选取。
下载链接如下:
VSCode 版本:https://marketplace.visualstudio.com/items?itemName=bradlc.vs...
IDEA 和 Webstorm 版本:https://plugins.jetbrains.com/plugin/15260-tailwind-intellisense
或者在代码编辑器的扩展市场中搜索即可。
Prettier 插件
在给元素编写许多类名时,有时会变得难以管理,难以区分哪些是自定义的,哪些用于响应式布局,以及哪些样式应该为了更好的理解而分组。比如,在编写一堆样式时,突然想起忘记为 flexbox
设置间隙,需要将其复制并粘贴到代码的不同位置。这种情况对我来说需要过多的思考。
为解决此类问题,Tailwind 团队推出了一个 Prettier 插件来为我们整理代码。使用它,只需运行:
npm i -D prettier prettier-plugin-tailwindcss
要使其工作,如果您之前没有使用过 prettier,请安装官方扩展,并配置一些您喜欢的快捷键来格式化文档:
如果你安装了 eslint,可能想将默认格式化程序更改为它。为此,请按 CTRL + Shift + P
,选择“使用格式化文档”,然后选择“配置默认格式化程序”,并从列表中选择 prettier。如果您之前使用了一些 eslint 选项,可以在 Prettier 的官方网站上阅读如何将这些规则与 prettier 集成。
这个插件的排序顺序如下:
- Tailwind 中未定义的类名,即用户自定义的类名
- Tailwind 中的宽度、弹性布局等其他样式,按其重要性排序
- 媒体查询和悬停效果
配置我们的项目
当刚安装 Tailwind 时,您可能会运行以下命令:
npx tailwindcss init -y
这会生成 Tailwind 配置文件。如果正确探索并填写它,它将非常有用!
逆向媒体查询
在开始任何 Tailwind 项目之前,首先要做的两件最重要的事情是:
使用逆向媒体查询。
制作响应式网站通常有两种基本方法:移动端优先和桌面端优先:
第一种方法首先从制作移动视图开始,然后调整至桌面视图,而另一种则恰好相反。
默认情况下,Tailwind 基于第一种方法,这就是为什么它的所有基本媒体查询都是 min-width
类型的,意味着需要在某个断点以下定义样式,然后使用 md
和其他变体类进行更改。
对我来说,这个决定有些麻烦,因为我习惯于从桌面视图开始制作网站。如果你和我一样,有一个小技巧可以使 Tailwind 按我们想要的方式工作,只需将以下内容添加到配置文件的 theme
中:
screens: {
'2xl': {'max': '1535px'},
// => @media (max-width: 1535px) { ... }
'xl': {'max': '1279px'},
// => @media (max-width: 1279px) { ... }
'lg': {'max': '1023px'},
// => @media (max-width: 1023px) { ... }
'md': {'max': '767px'},
// => @media (max-width: 767px) { ... }
'sm': {'max': '639px'},
// => @media (max-width: 639px) { ... }
}
完成后,所有断点都将从特定宽度开始工作,我们不需要反向思考了!
使用任意 REM 值
你可能已经知道,CSS 中有许多单位用来定义大小,如 pt、px、cm、em、rem
等。对我来说,这些单位中最有价值的是 rem
。rem
的默认大小是 16px
,即 HTML document的默认大小,这意味着当用户在浏览器中更改缩放比例时,我们的document 也会相应放大。
但你可能会问,当需要将所有 Figma 设计转换为这些值并花费大量时间时,如何使用这些值呢?为此,我们可以在主 CSS 文件中这样定义基本字体大小:
html {
font-size: 62.5%
}
从基本的 16 像素出发,62.5%
实际上是 10px
。有了它,我们可以简单地将所有像素值除以 10
,所以 100 变成 10,123 变成 12.3,0.523 变成 0.0523 —— 您需将小数点向左移动一位 —— 很简单!
那么,这与我们的 Tailwind 之旅有何关系呢?你可能已经注意到,将预定义的 Figma 项目转换为即时网站非常痛苦,因为尽管基本的 Tailwind 类非常周到,几乎适合所有设计,但找到每一个类都需要一些时间和思考。例如,我们在 Figma 设计中有一个 20px
的大小 —— 我们可以去 Tailwind 文档中找到相应的类,或者我们可以直接写:
class="px-[2rem]"
将值锁定在方括号中意味着我们可以在其中编写任何基本的 CSS,例如我们自己的值而不是预定义的值。没有其他!无需再搜索或四舍五入!
这是否会使您的包大小增加,因为定义任何任意值时 Tailwind 会创建一个附加类?不会,因为 Tailwind 是可摇树的 —— 这意味着所有基本类都从您的包中删除了,如果您没有使用它们 —— 对您来说更好!
那么可重用性如何呢?我们可以在配置中以与此处相同的方式定义自己的样式,或者使用 Tailwind 的基本样式,并且确实应该这样做!我建议将任意值的使用限制在间隙、特定宽度和高度以及任何不可预测且不适合设计系统的其他事物上。
使用这种方法使 Tailwind 智能提示注释变得过时了吗?实际上并不是,因为可以在 settings.json
中这样调整扩展的基本字体大小:
"tailwindCSS.rootFontSize": 10,
添加我们自己的插件
有时,Tailwind 存在一个问题:它允许我们使用基本的 CSS 构造,比如将某些样式应用于元素的所有子元素,但要使其工作,我们需要重写每个带有该构造的样式,这种方法完全违背了 DRY(不重复自己)原则。
为了更清楚地说明,我们来看一个基本的例子:
假设我们需要制作这样一个组件:
<nav className="flex flex-col gap-[1rem] text-center text-medium font-bold underline ">
<a href="/catland" className="rounded-medium bg-orange px-[1rem] py-[1.4rem] text-blue">
catland 32 apartment 69
</a>
<a href="/station" className="rounded-medium bg-orange px-[1rem] py-[1.4rem] text-blue-dark">
cat’s station in Cat York
</a>
<a href="/cafe" className="rounded-medium bg-orange px-[1rem] py-[1.4rem] text-blue-darkest">
cat cafe at Catfel Tower
</a>
</nav>
注意我们是如何重复自己的?如果我们以后需要更改这个类怎么办?当然,我们可以利用现代 IDE 的多重选择功能,但对我来说那太麻烦了。
.class > * {
@apply bg-orange rounded-medium px-[1rem] py-[1.4rem]
}
将会把这个样式应用到所有子元素上,而不需要重复。因此我们在 Tailwind 类中用 Arbitrary value 用法实现它:
<nav className="flex flex-col gap-[1rem] text-center text-medium font-bold underline [&>*]:rounded-medium [&>*]:bg-orange [&>*]:px-[1rem] [&>*]:py-[1.4rem]">
<a href="/catland" className="text-blue">
catland 32 apartment 69
</a>
<a href="/station" className="text-blue-dark">
cat’s station in Cat York
</a>
<a href="/cafe" className="text-blue-darkest">
cat cafe at Catfel Tower
</a>
</nav>
但这也有重复!如果我们以后需要使用这种结构,或者我们的一些同事根本不知道它,每次都会忘记怎么办?为了解决这个问题,有一个解决方案:Tailwind 插件。
在 Tailwind 配置中,我们可以定义我们自己的自定义属性,以后可以在基本函数中重用,如下所示:
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {},
plugins: [
function ({ addVariant }) {
addVariant("child", "& > *");
},
],
};
这里,我们说现在我们的代码将有一个名为 ‘child’ 的变体类,它将采用之前在 Arbitrary value 括号中定义的功能。
现在我们可以这样写:
<nav className="text-medium child:rounded-medium child:bg-orange child:px-[1rem] child:py-[1.4rem] flex flex-col gap-[1rem] text-center font-bold underline ">
<a href="/catland" className="text-blue">
catland 32 apartment 69
</a>
<a href="/station" className="text-blue-dark">
cat’s station in Cat York
</a>
<a href="/cafe" className="text-blue-darkest">
cat cafe at Catfel Tower
</a>
</nav>
使用 React 和 TypeScript 制作动态可复用组件
由于 React 和 TypeScript 的技术组合越来越受欢迎,我们将利用 Tailwind 制作一些酷炫的可复用按钮。
在此过程中,我们还将了解一些非常小的库来帮助我们:
Class-variance-authority
是一个用于制作可复用类的库,特别适合于 Tailwind,因为我们不需要在它之外定义任何东西 —— 一个安全、始终有效的工具!Tailwind-merge
和clsx
是两个帮助我们管理我们在class-variance-authority
定义中定义的类的库。它们的使用完全是可选的,因为它们只在一些罕见的情况下有用。
我们安装这些库,然后开始创建我们的按钮!
npm i tailwind-merge clsx class-variance-authority
首先,我们创建一个名为 Button.tsx
的文件,其中包含一个基本的按钮组件:
export const Button = () => {
return (
<button>
</button>
);
};
然后,我们用 class-variance-authority
定义我们的类。为此,从库中导入函数,如下所示:
import { cva } from "class-variance-authority";
函数接受两个参数:
- 适用于我们可复用按钮所有变体的基本类
- 包括我们定义的变体及其基本回退情况的对象(以防我们忘记定义某些内容)。
示例如下:
export const buttonVariants = cva(
"text-black transition-all border-[1px] border-solid focus:ring-2 text-small",
{
variants: {
variant: {
filled: "bg-yellow-600 ring-black border-yellow-600 hover:bg-white",
outline: "border-yellow-600 ring-black bg-white hover:bg-yellow-600",
},
size: {
small: "px-[1.5rem] py-[1rem] rounded-sm",
medium: "px-[2rem] py-[1.5rem] rounded-md",
},
},
defaultVariants: {
variant: "filled",
size: "small",
},
}
);
然后,我们定义默认变体:variant
将是 filled
,size
将是 small
。
由于我们的组件是可复用的,我们需要以某种方式将这些属性传递给它 —— 我们将通过 props 来做,并使它们类型安全。
首先,我们将声明一个基本接口:
interface IButtonProps {}
然后,我们将从 class-variance-authority
的泛型接口 VariantProps 扩展,可以通过 cva
函数导入它。它接受我们定义的 buttonVariants
的 typeof
并使 props
类型安全,因此我们不会定义我们没有的 big
尺寸:
interface IButtonProps extends VariantProps<typeof buttonVariants> {}
最后,我们还将从 React 的 HTMLAttributes
扩展,它也是泛型并接受 HTML 元素的类型。它将使我们的 props
包括按钮的所有基本 HTML 属性,如 onClick
,以及我们类型中定义的 React children:
interface IButtonProps
extends HTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
完成所有这些后,我们可以这样对组件进行类型化并使用其 props:
export const Button = ({
variant,
size,
className,
children,
...props
}: IButtonProps) => {
return (
<button className={buttonVariants({ size, variant })} {...props}>
{children}
</button>
);
};
现在,当我们传递 variant
和 size
时,按钮将改变其视图!看:
export default function App() {
return (
<>
<Button variant="filled">填充按钮</Button>
<Button variant="outline">轮廓按钮</Button>
</>
);
}
但是,当我们需要稍微调整按钮时怎么办?为此,我们有我们的 className
属性,可以用两种不同的方式使用:
我们在现场将其与我们的 buttonVariants
函数合并,使用模板字面量:
<button className={`${buttonVariants({ size, variant })} ${className}`} {...props}>
我们使用之前提到的两个库来确保我们的类没有重复:例如,当我们有 mx-2
和 my-2
时,它将被转换为 m-2
。
为此,让我们在其他地方定义一个小函数,如下所示:
import clsx, { ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export const clsxm = (...classes: ClassValue[]) => twMerge(clsx(...classes));
这里,它接受任何数量的类并将它们与 tailwind-merge
库中的函数合并。现在,我们可以用该函数而不是模板字面量合并我们的类:
<button
className={twMerge(buttonVariants({ size, variant }), className)}
{...props}
>
完成!
需要注意的一点 —— class-variance-authority
方法制作可复用组件的最佳之处在于,我们能够在应用程序的任何其他地方重用我们收到的函数,这就是为什么我们要导出它的原因。在这里,最常见的用途可能是需要为某个 <a>
链接设置样式,可以轻松实现:只需导入函数并定义类即可!
import { buttonVariants } from "./Button/Button";
export default function App() {
return (
<>
<a className={buttonVariants({ size: "medium", variant: "filled" })}>
中等大小填充链接!
</a>
</>
);
}
最后,如果您一直在跟随我的示例,您可能已经注意到,当我们在 cva
函数内编写类时,Tailwind 智能提示扩展不起作用。这是因为它没有被训练去做这件事 —— 它只在我们编写类名和使用 @apply
样式时触发。为了解决这个问题,我们可以稍微调整 settings.json
文件,添加以下行:
"tailwindCSS.experimental.classRegex": [
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
],
交流
首发于公众号 大迁世界,欢迎关注。📝 每周一篇实用的前端文章 🛠️ 分享值得关注的开发工具 ❓ 有疑问?我来回答
本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。