在奇舞精选之前发布的一篇文章里盘点了
2024 年最受欢迎的前端项目
,其中 shadcn/ui 和去年一样再次成为2024年最热门的项目。这篇文章我们将对 shadcn/ui 进行简单介绍。
什么是Shadcn
官网:https://ui.shadcn.com/
Github:https://github.com/shadcn-ui/ui
在 shadcn 文档 https://ui.shadcn.com/docs,可以看到相关介绍:
shadcn/ui
不是组件库,而是可重复使用的组件的集合
开发者可以将其
复制粘贴
到您的应用中,而不是作为依赖项进行安装
shadcn/ui 目前包括了常用的组件,并且在持续更新中。
接下来:我们初始化一个 Next.js 项目,然后使用
shadcn/ui 来理解上面的定义:
初始化Next.js项目
npx create-next-app@latest Need to install the following packages: [email protected] Ok to proceed? (y) ✔ What is your project named? … shadcn-ui-example ✔ Would you like to use TypeScript? … No / Yes ✔ Would you like to use ESLint? … No / Yes ✔ Would you like to use Tailwind CSS? … No / Yes ✔ Would you like your code inside a `src/` directory? … No / Yes ✔ Would you like to use App Router? (recommended) … No / Yes ✔ Would you like to use Turbopack for `next dev`? … No / Yes ✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes Creating a new Next.js app in /shadcn-ui-example.
安装
shadcn/ui
npx shadcn@latest init -d
将组件添加到项目
npx shadcn@latest add button
在 package.json 中我们并没有找到关于 button 的依赖:
而是在 shadcn-ui-example/components/ui 目录下,生成了一个 button.jsx。
button.jsx 具体代码如下:
import * as React from "react" import { Slot } from "@radix-ui/react-slot" import { cva } from "class-variance-authority" ; import { cn } from "@/lib/utils" const buttonVariants = cva( "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0" , { variants : { variant : { default : "bg-primary text-primary-foreground shadow hover:bg-primary/90" , destructive : "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90" , outline : "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground" , secondary : "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80" , ghost : "hover:bg-accent hover:text-accent-foreground" , link : "text-primary underline-offset-4 hover:underline" , }, size : { default
: "h-9 px-4 py-2" , sm : "h-8 rounded-md px-3 text-xs" , lg : "h-10 rounded-md px-8" , icon : "h-9 w-9" , }, }, defaultVariants : { variant : "default" , size : "default" , }, } ) const Button = React.forwardRef( ( { className, variant, size, asChild = false , ...props }, ref ) => { const Comp = asChild ? Slot : "button" return ( ( < Comp className = {cn(buttonVariants({ variant , size , className }))} ref = {ref} { ...props } /> ) ); }) Button.displayName = "Button" export { Button, buttonVariants }
可以看到 shadcn/ui 直接把组件代码增加的了我们的项目中。这样的好处显而易见,对比我们使用类似 Ant Design 之类的组件库,如果想要高度定制样式 shadcn/ui 会更容易一些。我们把button添加到页面中运行看一下效果:
"use client" ; import { Button } from "@/components/ui/button" ; export default function Home ( ) { return ( < div className = "flex w-full h-full items-center justify-center" > < Button > Click me Button > div > ); }
我们再添加一个
Accordion 组件:
npx shadcn@latest add accordion
"use client" import * as React from "react" import * as AccordionPrimitive from "@radix-ui/react-accordion" import { ChevronDown } from "lucide-react" import { cn } from "@/lib/utils" const Accordion = AccordionPrimitive.Root const AccordionItem = React.forwardRef( ( { className, ...props }, ref ) => ( < AccordionPrimitive.Item ref = {ref} className = {cn( " border-b ", className )} { ...props } /> )) AccordionItem.displayName = "AccordionItem" const AccordionTrigger = React.forwardRef( ( { className, children, ...props }, ref ) => ( < AccordionPrimitive.Header className = "flex" > < AccordionPrimitive.Trigger ref = {ref} className = {cn( " flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline text-left [&[ data-state = open] > svg]:rotate-180", className )} {...props}> {children} < ChevronDown className = "h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" /> AccordionPrimitive.Trigger > AccordionPrimitive.Header > )) AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName const AccordionContent = React.forwardRef( ( { className, children, ...props }, ref ) => ( < AccordionPrimitive.Content ref = {ref} className = "overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{ ...props }> < div className = {cn( " pb-4 pt-0 ", className )}> {children} div > AccordionPrimitive.Content > )) AccordionContent.displayName = AccordionPrimitive.Content.displayName export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
这次在 package.json 中添加了 radix-ui 相关依赖:
"@radix-ui/react-accordion" : "^1.2.2"
什么是radix-ui
官网:https://www.radix-ui.com
Github:https://github.com/radix-ui/primitives
Radix 提供了无样式、访问性友好的 React 原子级组件,允许开发者在完全掌控样式的情况下构建自己的设计系统。
Radix 的核心功能
Unstyled
Radix UI 组件本身不带有任何设计或样式,它只提供了基础的结构和交互功能。开发者可以完全自定义样式,适应自己的设计需求。
Stateless
Radix 组件不维护内部状态,而是依赖外部传入的 props 来管理组件的状态。
Accessibility
Radix UI 对可访问性有很高的关注,所有组件都遵循 WCAG(Web Content Accessibility Guidelines)标准。
高度可定制
Radix UI 组件提供了许多灵活的 API,可以根据项目需求进行定制。
原子化
Radix 提供的组件是高度模块化的,可以按需引入。每个组件都只处理单一的功能,避免了将多个功能捆绑到一起的复杂性,增强了代码的复用性和可维护性。
Shadcn、Radix UI、Ant Design对比
特性
Shadcn
Radix UI
Ant Design
定位
可定制性
极高,依赖 Tailwind CSS,完全控制样式
开发效率
适用场景
需要灵活样式控制的项目,尤其是基于 Tailwind CSS 的应用
Shadcn
定制主题
在 https://ui.shadcn.com/themes 选择主题然后点击 Copy code 即可:
@layer base { :root { --background: 0 0 % 100 %; --foreground: 0 0 % 3.9 %; --card: 0 0 % 100 %; --card-foreground: 0 0 % 3.9 %; --popover: 0 0 % 100 %; --popover-foreground: 0 0 % 3.9 %; --primary: 0 72.2 % 50.6 %; --primary-foreground: 0 85.7 % 97.3 %; --secondary: 0 0 % 96.1 %; --secondary-foreground: 0 0 % 9 %; --muted: 0 0 % 96.1 %; --muted-foreground: 0 0 % 45.1 %; --accent: 0 0 % 96.1 %; --accent-foreground: 0 0 % 9 %; --destructive: 0 84.2 % 60.2 %; --destructive-foreground: 0 0 % 98 %; --border: 0 0 % 89.8 %; --input: 0 0 % 89.8 %; --ring: 0 72.2 % 50.6 %; --radius: 0.5 rem; --chart -1 : 12 76 % 61 %; --chart -2 : 173 58 % 39 %; --chart -3 : 197 37 % 24 %; --chart -4 : 43 74 % 66 %; --chart -5 : 27 87 % 67 %; } .dark { --background: 0 0 % 3.9 %; --foreground: 0 0 % 98 %; --card: 0 0 % 3.9 %; --card-foreground: 0 0 % 98 %; --popover: 0 0 % 3.9 %; --popover-foreground: 0 0 % 98 %; --primary: 0 72.2 % 50.6 %; --primary-foreground: 0 85.7 % 97.3 %; --secondary: 0 0 % 14.9 %; --secondary-foreground: 0 0 % 98 %; --muted: 0