使用 Tailwind 構建可重用的 React 組件

已發表: 2022-03-10
快速總結 ↬ Tailwind 是一個流行的實用程序優先 CSS 框架,它為 Web 開發人員提供低級類名稱。 它沒有任何 JavaScript,並且可以很好地與 React、Vue、Angular、Ember 等現有框架配合使用。 雖然這是積極的,但對於新開發人員來說,理解如何將 Tailwind 集成到他們的應用程序中可能會令人困惑。 在本文中,我們將探索使用 Tailwind 構建可重用 React 組件的方法。

在這篇文章中,我們將介紹幾種不同的方法來構建可重用的 React 組件,這些組件在後台利用 Tailwind,同時向其他組件公開一個漂亮的接口。 這將通過從長長的類名列表轉移到更易於閱讀和維護的語義道具來改進您的代碼。

你需要使用過 React 才能很好地理解這篇文章。

Tailwind 是一個非常流行的 CSS 框架,它提供低級實用程序類來幫助開發人員構建自定義設計。 它在過去幾年中越來越受歡迎,因為它很好地解決了兩個問題:

  1. Tailwind 可以輕鬆地對 HTML 進行迭代更改,而無需通過樣式表來查找匹配的 CSS 選擇器。
  2. Tailwind 有合理的約定和默認設置。 這使人們無需從頭開始編寫 CSS 即可輕鬆上手。

添加綜合文檔,Tailwind 如此受歡迎也就不足為奇了。

這些方法將幫助您轉換如下所示的代碼:

 <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> Enable </button>

要編寫如下所示的代碼:

 <Button size="sm" textColor="white" bgColor="blue-500"> Enable </Button>

兩個片段之間的區別在於,第一個片段使用了標準的 HTML 按鈕標籤,而第二個片段使用了<Button>組件。 <Button>組件是為了可重用性而構建的,並且由於它具有更好的語義而更易於閱讀。 它不是使用一長串類名,而是使用屬性來設置各種屬性,例如sizetextColorbgColor

讓我們開始吧。

跳躍後更多! 繼續往下看↓

方法 1:使用 Classnames 模塊控制類

將 Tailwind 適配到 React 應用程序的一種簡單方法是包含類名並以編程方式切換它們。

classnames npm 模塊使在 React 中切換類變得容易。 為了演示如何使用它,讓我們舉一個用例,您的 React 應用程序中有<Button>組件。

 // This could be hard to read. <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Enable</button> // This is more conventional React. <Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>

讓我們看看如何分離 Tailwind 類,以便使用此<Button>組件的人可以使用 React 道具,例如sizetextColorbgColor

  1. bgColortextColor等 props 直接傳遞到類名字符串模板中。
  2. 使用對像以編程方式切換類名(就像我們使用size屬性所做的那樣)

在下面的示例代碼中,我們將看看這兩種方法。

 // Button.jsx import classnames from 'classnames'; function Button ({size, bgColor, textColor, children}) { return ( <button className={classnames("bg-${bgColor} text-${textColor} font-bold py-2 px-4 rounded", { "text-xs": size === 'sm' "text-xl": size === 'lg', })}> {children} </button> ) }; export default Button;

在上面的代碼中,我們定義了一個Button組件,它採用以下 props:

  • size
    定義按鈕的大小並應用 Tailwind 類text-xstext-xl
  • bgColor
    定義按鈕的背景顏色並應用 Tailwind bg-*類。
  • textColor
    定義按鈕的文本顏色並應用 Tailwind text-* classes
  • children
    任何子組件都將在這里傳遞。 它通常包含<Button>中的文本。

通過定義Button.jsx ,我們現在可以將其導入並使用 React props 而不是類名。 這使我們的代碼更易於閱讀和重用。

 import Button from './Button'; <Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>

為交互組件使用類名

Button 是一個非常簡單的用例。 更複雜的事情呢? 好吧,您可以進一步製作交互式組件。

例如,讓我們看一下使用 Tailwind 製作的下拉菜單。


使用 Tailwind 和類名切換構建的交互式下拉列表。

對於這個示例,我們使用 Tailwind CSS 類名創建 HTML 組件,但我們公開了一個如下所示的 React 組件:

 <Dropdown options={\["Edit", "Duplicate", "Archive", "Move", "Delete"\]} onOptionSelect={(option) => { console.log("Selected Option", option)} } />

查看上面的代碼,您會注意到我們沒有任何 Tailwind 類。 它們都隱藏在<Dropdown/>的實現代碼中。 這個Dropdown組件的用戶只需要提供一個options列表和一個單擊處理程序,當單擊一個optiononOptionSelect

讓我們看看如何使用 Tailwind 構建這個組件。

去掉一些不相關的代碼,這裡就是邏輯的癥結所在。 您可以查看此 Codepen 以獲取完整示例。

 import classNames from 'classnames'; function Dropdown({ options, onOptionSelect }) { // Keep track of whether the dropdown is open or not. const [isActive, setActive] = useState(false); const buttonClasses = `inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-blue-500 active:text-gray-200 transition ease-in-out duration-150`; return ( // Toggle the dropdown if the button is clicked <button onClick={() => setActive(!isActive)} className={buttonClasses}> Options </button> // Use the classnames module to toggle the Tailwind .block and .hidden classes <div class={classNames("origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg", { block: isActive, hidden: !isActive })}> // List items are rendered here. {options.map((option) => <div key={option} onClick={(e) => onOptionSelect(option)}>{option}</div>)} </div> ) } export default Dropdown;

下拉菜單通過使用.hidden.block類有選擇地顯示或隱藏來實現交互。 每當按下<button>時,我們都會觸發切換isActive狀態的onClick處理程序。 如果按鈕處於活動狀態( isActive === true ),我們設置block類。 否則,我們設置hidden類。 這些都是用於切換顯示行為的 Tailwind 類。

總之,classnames 模塊是一種以編程方式控制 Tailwind 類名的簡單而有效的方法。 它使將邏輯分離到 React props 中變得更容易,從而使您的組件更易於重用。 它適用於簡單的交互式組件。

方法 2:使用常量定義設計系統

另一種同時使用 Tailwind 和 React 的方法是使用常量並將 props 映射到特定常量。 這對於構建設計系統是有效的。 讓我們用一個例子來演示。

從列出設計系統的theme.js文件開始。

 // theme.js (you can call it whatever you want) export const ButtonType = { primary: "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded", secondary: "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded", basic: "bg-white hover:bg-gray-700 text-gray-700 font-bold rounded", delete: "bg-red-300 hover:bg-red-500 text-white font-bold rounded" }; export const ButtonSize = { sm: "py-2 px-4 text-xs", lg: "py-3 px-6 text-lg" }

在這種情況下,我們有兩組常量:

  • ButtonType定義了我們應用程序中按鈕的樣式。
  • ButtonSizes定義了我們應用程序中按鈕的大小。

現在,讓我們編寫我們的<Button>組件:

 import {ButtonType, ButtonSize} from './theme'; function Button({size, type, children}) { // This can be improved. I'm keeping it simple here by joining two strings. const classNames = ButtonType[type] + " " + ButtonSize[size]; return ( <button className={classNames}>{children}</button> ) } export default Button;

我們使用ButtonTypeButtonSize常量來創建類名列表。 這使我們的<Button>的界面更加美觀。 它讓我們可以使用sizetype屬性,而不是將所有內容都放在類名字符串中。

 // Cleaner and well defined props. <Button size="xs" type="primary">Enable</Button>

與之前的方法相比:

 // Exposing class names <button className="py-2 px-4 text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold rounded">Enable</button>

如果您需要重新定義應用程序中按鈕的外觀,只需編輯theme.js文件,應用程序中的所有按鈕都會自動更新。 這比在各種組件中搜索類名更容易。

方法 3:使用@apply組合實用程序

第三種提高 React 組件易讀性的方法是使用 CSS 和 PostCSS 中可用的@apply模式來提取重複的類。 這種模式涉及使用樣式表和後處理器。

讓我們通過一個例子來演示它是如何工作的。 假設您有一個包含主要和次要按鈕的按鈕組。

由主按鈕和輔助按鈕組成的按鈕組
一個按鈕組,由一個主要按鈕和一個次要按鈕組成。 (大預覽)
 <button className="py-2 px-4 mr-4 text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold rounded">Update Now</button> <button className="py-2 px-4 text-xs mr-4 hover:bg-gray-100 text-gray-700 border-gray-300 border font-bold rounded">Later</button>

使用@apply模式,您可以將此 HTML 編寫為:

 <button className="btn btn-primary btn-xs">Update Now</button> <button className="btn btn-secondary btn-xs">Later</button>

然後可以將其應用於 React 成為:

 import classnames from "classnames"; function Button ({size, type, children}) { const bSize = "btn-" + size; const bType = "btn-" + type; return ( <button className={classnames("btn", bSize, bType)}>{children}</button> ) } Button.propTypes = { size: PropTypes.oneOf(['xs, xl']), type: PropTypes.oneOf(['primary', 'secondary']) }; // Using the Button component. <Button type="primary" size="xs">Update Now</Button> <Button type="secondary" size="xs">Later</Button>

下面介紹如何創建這些 BEM 樣式的類名,例如.btn.btn-primary等。 首先創建一個button.css文件:

 /\* button.css \*/ @tailwind base; @tailwind components; .btn { @apply py-2 px-4 mr-4 font-bold rounded; } .btn-primary { @apply bg-blue-500 hover:bg-blue-700 text-white; } .btn-secondary { @apply hover:bg-gray-700 text-gray-700 border-gray-300 border; } .btn-xs { @apply text-xs; } .btn-xl { @apply text-xl; } @tailwind utilities;

上面的代碼不是真正的CSS,但它會被 PostCSS 編譯。 這裡有一個 GitHub 存儲庫,它展示瞭如何為 JavaScript 項目設置 PostCSS 和 Tailwind。

還有一個簡短的視頻演示瞭如何在此處進行設置。

使用@apply的缺點

將 Tailwind 實用程序類提取到更高級別的 CSS 類中的概念似乎很有意義,但它有一些您應該注意的缺點。 讓我們用另一個例子來強調這些。

首先,通過提取這些類名,我們會丟失一些信息。 例如,我們需要知道.btn-primary必須添加到已經應用了.btn的組件中。 此外, .btn-primary.btn-secondary不能一起應用。 僅通過查看類,這些信息並不明顯。

如果這個組件更複雜,您還需要了解類之間的父子關係。 在某種程度上,這是 Tailwind 旨在解決的問題,通過使用@apply ,我們以不同的方式將問題帶回來。

這是一個視頻,其中 Tailwind 的創建者 Adam Wathan 深入探討了使用@apply的利弊。

概括

在本文中,我們研究了將 Tailwind 集成到 React 應用程序以構建可重用組件的三種方法。 這些方法可幫助您使用props構建具有更簡潔界面的 React 組件。

  1. 使用類名模塊以編程方式切換類。
  2. 定義一個常量文件,您可以在其中定義每個組件狀態的類列表。
  3. 使用@apply提取更高級別的 CSS 類。

如果您有任何問題,請在 Twitter 上@tilomitra 上給我發消息。

SmashingMag推薦閱讀

  • 在 React 項目中設置 Tailwind CSS
  • 使用 React 創建可排序表
  • Firefox 中新的和實驗性的 CSS DevTools 指南
  • 製作您自己的擴展和收縮內容面板