TypeScript 使用 UnoCSS 的复杂案例:构建一个可定制的数据表格组件
Claude 3.5 的例子 您说得对,我们可以创建一个更复杂的示例来充分展示 Bun 环境下使用 UnoCSS 的特点。让我们构建一个更高级的应用,它将包含动态主题切换、自定义动画、响应式设计,以及与 Bun 的特性结合使用。
高级 Bun + UnoCSS 示例:构建一个交互式仪表板
这个示例将创建一个交互式仪表板,包含以下特性:
- 动态主题切换(明/暗模式)
- 自定义动画
- 响应式设计
- 实时数据更新(使用 Bun 的内置 WebSocket)
- 服务端渲染(SSR)与 Bun 的性能优化
步骤 1: 项目设置
bun init dashboard-app
cd dashboard-app
bun add unocss @unocss/preset-uno @unocss/preset-wind @unocss/preset-attributify @unocss/preset-icons @unocss/preset-mini react react-dom步骤 2: UnoCSS 配置
创建 uno.config.ts:
import { defineConfig, presetUno, presetWind, presetAttributify, presetIcons, presetMini } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetWind(),
presetAttributify(),
presetIcons(),
presetMini(),
],
theme: {
colors: {
primary: {
light: '#60a5fa',
DEFAULT: '#3b82f6',
dark: '#2563eb',
},
},
},
shortcuts: {
'btn': 'py-2 px-4 font-semibold rounded-lg shadow-md transition duration-300 ease-in-out transform hover:scale-105',
'btn-primary': 'btn bg-primary text-white hover:bg-primary-dark',
'card': 'bg-white dark:bg-gray-800 rounded-lg shadow-md p-6',
},
rules: [
['custom-animation', { animation: 'custom 1s ease-in-out infinite' }],
],
})步骤 3: 创建组件
- 创建
src/components/ThemeToggle.tsx:
import React from 'react'
export const ThemeToggle = () => {
const toggleTheme = () => {
document.documentElement.classList.toggle('dark')
}
return (
<button onClick={toggleTheme} className="btn-primary">
Toggle Theme
</button>
)
}- 创建
src/components/Dashboard.tsx:
import React, { useState, useEffect } from 'react'
import { ThemeToggle } from './ThemeToggle'
interface DashboardData {
users: number
revenue: number
orders: number
}
export const Dashboard = () => {
const [data, setData] = useState<DashboardData>({ users: 0, revenue: 0, orders: 0 })
useEffect(() => {
const ws = new WebSocket('ws://localhost:3000')
ws.onmessage = (event) => {
setData(JSON.parse(event.data))
}
return () => ws.close()
}, [])
return (
<div className="min-h-screen bg-gray-100 dark:bg-gray-900 transition-colors duration-500">
<div className="container mx-auto px-4 py-8">
<div className="flex justify-between items-center mb-8">
<h1 className="text-4xl font-bold text-gray-800 dark:text-white">Dashboard</h1>
<ThemeToggle />
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="card custom-animation">
<h2 className="text-2xl font-semibold mb-2">Users</h2>
<p className="text-4xl font-bold text-primary">{data.users}</p>
</div>
<div className="card custom-animation">
<h2 className="text-2xl font-semibold mb-2">Revenue</h2>
<p className="text-4xl font-bold text-primary">${data.revenue}</p>
</div>
<div className="card custom-animation">
<h2 className="text-2xl font-semibold mb-2">Orders</h2>
<p className="text-4xl font-bold text-primary">{data.orders}</p>
</div>
</div>
</div>
</div>
)
}步骤 4: 创建服务器
创建 src/server.ts:
import { serve } from 'bun'
import { renderToString } from 'react-dom/server'
import { Dashboard } from './components/Dashboard'
const dashboardHtml = renderToString(<Dashboard />)
const server = serve({
port: 3000,
fetch(req) {
if (req.url.endsWith('/ws')) {
const upgraded = server.upgrade(req)
if (upgraded) {
setInterval(() => {
upgraded.send(JSON.stringify({
users: Math.floor(Math.random() * 1000),
revenue: Math.floor(Math.random() * 10000),
orders: Math.floor(Math.random() * 100)
}))
}, 1000)
return
}
}
return new Response(
`<!DOCTYPE html>
<html>
<head>
<title>Dashboard</title>
<script src="https://unpkg.com/unocss"></script>
<script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
import { Dashboard } from './components/Dashboard.tsx'
createApp(Dashboard).mount('#app')
</script>
</head>
<body>
<div id="app">${dashboardHtml}</div>
</body>
</html>`,
{
headers: {
'Content-Type': 'text/html',
},
}
)
},
})
console.log(`Server running at http://localhost:${server.port}`)步骤 5: 运行应用
bun run src/server.ts解释
-
动态主题切换:使用 UnoCSS 的
dark:变体和 CSS 变量实现明暗主题切换。 -
自定义动画:在 UnoCSS 配置中定义了自定义动画规则
custom-animation。 -
响应式设计:使用 UnoCSS 的响应式工具类(如
md:grid-cols-3)实现响应式布局。 -
实时数据更新:利用 Bun 的内置 WebSocket 功能实现实时数据更新。
-
服务端渲染(SSR):使用 Bun 的
renderToString函数实现服务端渲染,提高首屏加载速度。 -
性能优化:Bun 的高性能特性自然地应用于整个应用程序,包括快速的 JavaScript 执行和高效的文件系统操作。
这个复杂的例子展示了如何在 Bun 环境中充分利用 UnoCSS 的强大功能,同时结合 Bun 的特性来创建一个高性能、实时更新的交互式仪表板。它涵盖了现代 web 应用开发中的多个方面,包括样式、交互、实时数据和服务端渲染。
Perplexity 例子
要在 Bun 环境下使用 UnoCSS,以下是一个复杂的实例,展示如何利用 Bun 的性能优势和 UnoCSS 的灵活性来构建一个功能齐全的 Web 应用程序。
步骤 1: 初始化项目
-
安装 Bun:
curl -fsSL https://bun.sh/install | bash -
创建新项目:
bun init my-unocss-app cd my-unocss-app -
安装依赖:
bun add react react-dom @unocss/preset-uno @unocss/preset-attributify @unocss/preset-icons unocss
步骤 2: 配置 UnoCSS
- 创建 UnoCSS 配置文件:
// uno.config.ts import { defineConfig, presetAttributify, presetIcons, presetUno } from 'unocss' export default defineConfig({ presets: [ presetUno(), presetAttributify(), presetIcons({ scale: 1.2, }), ], theme: { colors: { primary: '#3b82f6', secondary: '#6b7280', }, }, rules: [ ['m-1', { margin: '1px' }], ['m-2', { margin: '2px' }], ['text-lg', { fontSize: '18px' }], // 添加更多自定义规则根据需要 ], shortcuts: [ ['btn', 'px-4 py-2 rounded-md bg-blue-500 text-white cursor-pointer'], ['btn-blue', 'btn bg-blue-500 hover:bg-blue-700'], ['btn-red', 'btn bg-red-500 hover:bg-red-700'], ], })
步骤 3: 创建 React 应用
-
安装 React:
bun add react react-dom -
创建组件:
// src/components/Navbar.tsx import React from 'react' export const Navbar = () => { return ( <nav className="bg-primary p-4 text-white"> <h1 className="text-3xl">My App</h1> </nav> ) } -
创建主页组件:
// src/components/Home.tsx import React from 'react' import { Button } from './Button' export const Home = () => { return ( <div className="container mx-auto p-4"> <h1 className="text-5xl mb-4">Welcome to My App</h1> <Button variant="blue" onClick={() => alert('Blue button clicked')}> Blue Button </Button> <Button variant="red" className="mt-4" onClick={() => alert('Red button clicked')}> Red Button </Button> </div> ) } -
创建按钮组件:
// src/components/Button.tsx import React from 'react' export const Button = ({ children, variant = 'blue', ...props }: { children: React.ReactNode; variant?: 'blue' | 'red' }) => { return ( <button className={`btn-${variant} ${props.className || ''}`} {...props} > {children} </button> ) }
步骤 4: 使用 UnoCSS 和 React
-
创建应用入口:
// src/App.tsx import React from 'react' import { Navbar } from './components/Navbar' import { Home } from './components/Home' const App = () => { return ( <div> <Navbar /> <Home /> </div> ) } export default App -
创建主入口文件:
// src/index.tsx import React from 'react' import ReactDOM from 'react-dom' import App from './App' ReactDOM.render(<App />, document.getElementById('root'))
步骤 5: 运行应用
-
创建 HTML 文件:
<!-- public/index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My App</title> </head> <body> <div id="root"></div> <script type="module" src="/src/index.tsx"></script> </body> </html> -
配置 Bun 运行环境:
// package.json { "name": "my-unocss-app", "version": "0.1.0", "scripts": { "dev": "bun run dev", "build": "bun build", "start": "bun start" }, "dependencies": { "@unocss/preset-attributify": "^0.0.5", "@unocss/preset-icons": "^0.0.5", "@unocss/preset-uno": "^0.0.5", "react": "^18.2.0", "react-dom": "^18.2.0", "unocss": "^0.19.4" } } -
运行开发服务器:
bun run dev
解释
- UnoCSS 配置:在
uno.config.ts中定义了 UnoCSS 的预设和自定义规则,包括颜色主题、边距、字体大小等。 - React 组件:创建了多个 React 组件,例如
Navbar、Home和Button,并使用 UnoCSS 的类名来样式化这些组件。 - Bun 环境:利用 Bun 的性能优势快速运行和构建 React 应用,包括热重载和开发服务器支持。
这个示例展示了如何在 Bun 环境下使用 UnoCSS 来构建一个功能齐全的 Web 应用程序,利用 UnoCSS 的灵活性和可扩展性来管理样式,并利用 Bun 的高性能来加速开发和运行过程。
这个案例将会展示如何使用 UnoCSS 和 TypeScript 构建一个具有以下特性的可定制数据表格组件:
- 动态列: 列定义可以通过配置数组传递,支持自定义列名、数据类型、排序和格式化。
- 排序: 点击表头可以对数据进行排序,支持升序、降序和默认排序。
- 分页: 支持分页显示数据,可以自定义每页显示条数。
- 搜索: 支持对表格数据进行搜索过滤。
- 主题: 可以切换明暗主题。
1. 项目设置
bunx create-react-app my-table --template typescript
cd my-table
bun add @unocss/preset-uno @unocss/preset-attributify @unocss/preset-icons @types/react-table @tanstack/react-table uno.config.ts2. 配置 UnoCSS
// uno.config.ts
import { defineConfig, presetAttributify, presetIcons, presetUno } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetAttributify(),
presetIcons({
scale: 1.2,
}),
],
theme: {
// 自定义主题颜色
colors: {
primary: '#3b82f6',
},
},
})3. 创建表格组件
// src/components/Table.tsx
import React, { useMemo, useState } from 'react'
import { useTable, useSortBy, usePagination, useGlobalFilter } from '@tanstack/react-table'
interface Column {
header: string
accessor: string
type?: 'string' | 'number' | 'date'
format?: (value: any) => string
}
interface Data {
[key: string]: any
}
interface Props<T extends Data> {
columns: Column[]
data: T[]
}
export const Table = <T extends Data>({ columns, data }: Props<T>) => {
const [globalFilter, setGlobalFilter] = useState('')
const tableInstance = useTable(
{
columns: useMemo(() => columns, [columns]),
data,
globalFilter,
},
useGlobalFilter,
useSortBy,
usePagination,
)
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
prepareRow,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
state: { pageIndex, pageSize, globalFilter: filter },
} = tableInstance
return (
<div>
<div className="flex items-center justify-between mb-4">
<input
type="text"
value={filter || ''}
onChange={e => setGlobalFilter(e.target.value)}
placeholder="Search..."
className="border border-gray-300 rounded px-3 py-2"
/>
<select
value={pageSize}
onChange={e => {
setPageSize(Number(e.target.value))
}}
className="border border-gray-300 rounded px-3 py-2"
>
{[10, 25, 50, 100].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
<table {...getTableProps()} className="w-full text-left table-auto">
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th
{...column.getHeaderProps(column.getSortByToggleProps())}
className="px-6 py-3 border-b text-xs font-medium uppercase tracking-wider"
>
{column.render('Header')}
{column.isSorted ? (
column.isSortedDesc ? (
<i className="i-carbon-chevron-down ml-2"></i>
) : (
<i className="i-carbon-chevron-up ml-2"></i>
)
) : null}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()} className={i % 2 === 0 ? 'bg-gray-50' : ''}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()} className="px-6 py-4 border-b">
{cell.column.format ? cell.column.format(cell.value) : cell.render('Cell')}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
<div className="flex items-center justify-between mt-4">
<div>
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage} className="mr-2">
{'<<'}
</button>
<button onClick={() => previousPage()} disabled={!canPreviousPage} className="mr-2">
{'<'}
</button>
<span className="mr-2">
Page{' '}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{' '}
</span>
<button onClick={() => nextPage()} disabled={!canNextPage} className="mr-2">
{'>'}
</button>
<button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
{'>>'}
</button>
</div>
</div>
</div>
)
}4. 使用表格组件
// src/App.tsx
import React from 'react'
import { Table } from './components/Table'
const columns = [
{ header: 'Name', accessor: 'name' },
{ header: 'Age', accessor: 'age', type: 'number' },
{
header: 'Date',
accessor: 'date',
type: 'date',
format: (value: Date) => new Date(value).toLocaleDateString(),
},
]
const data = [
{ name: 'John Doe', age: 30, date: new Date() },
{ name: 'Jane Doe', age: 25, date: new Date() },
{ name: 'Peter Pan', age: 18, date: new Date() },
]
const App = () => {
return (
<div className="container mx-auto p-4">
<Table columns={columns} data={data} />
</div>
)
}
export default App5. 运行项目
bun run dev解释
- UnoCSS: 我们使用 UnoCSS 来快速添加样式,例如
table-auto,border-gray-300,bg-gray-50等。 - @tanstack/react-table: 这个库提供了强大的表格功能,包括排序、分页和过滤。
- TypeScript: 我们使用 TypeScript 来定义接口和类型,提高代码的可读性和可维护性。
这个案例展示了如何使用 UnoCSS 和 TypeScript 构建一个功能丰富、可定制的数据表格组件。你可以根据自己的需求扩展这个组件,例如添加行选择、编辑功能等.
好的,让我们使用 Bun 运行环境来创建一个使用 UnoCSS 的复杂案例。我们将构建一个交互式的仪表板组件,包含多个小部件,展示 UnoCSS 的各种特性和灵活性。
首先,让我们设置项目:
bun create vite my-unocss-dashboard --template react-ts
cd my-unocss-dashboard
bun add unocss @unocss/reset
bun add @iconify-json/carbon现在,让我们配置 UnoCSS:
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import UnoCSS from 'unocss/vite'
export default defineConfig({
plugins: [
UnoCSS(),
react()
],
})// uno.config.ts
import { defineConfig, presetUno, presetIcons, presetAttributify } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetIcons({
scale: 1.2,
cdn: 'https://esm.sh/'
}),
presetAttributify(),
],
theme: {
colors: {
primary: '#3b82f6',
secondary: '#10b981',
accent: '#8b5cf6',
},
},
shortcuts: {
'btn': 'py-2 px-4 font-semibold rounded-lg shadow-md',
'btn-primary': 'btn bg-primary text-white hover:bg-primary-700',
'card': 'bg-white rounded-lg shadow-md p-4',
},
})现在,让我们创建一些组件:
// src/components/Header.tsx
import React from 'react'
const Header: React.FC = () => {
return (
<header className="bg-primary text-white p-4">
<h1 className="text-2xl font-bold">UnoCSS Dashboard</h1>
</header>
)
}
export default Header
// src/components/Sidebar.tsx
import React from 'react'
const Sidebar: React.FC = () => {
return (
<aside className="bg-gray-100 w-64 p-4 h-screen">
<nav>
<ul className="space-y-2">
<li><a href="#" className="block p-2 hover:bg-gray-200 rounded">Dashboard</a></li>
<li><a href="#" className="block p-2 hover:bg-gray-200 rounded">Analytics</a></li>
<li><a href="#" className="block p-2 hover:bg-gray-200 rounded">Settings</a></li>
</ul>
</nav>
</aside>
)
}
export default Sidebar
// src/components/Widget.tsx
import React from 'react'
interface WidgetProps {
title: string
value: string | number
icon: string
color: string
}
const Widget: React.FC<WidgetProps> = ({ title, value, icon, color }) => {
return (
<div className={`card ${color} text-white`}>
<div className="flex justify-between items-center">
<div>
<h3 className="text-lg font-semibold">{title}</h3>
<p className="text-3xl font-bold">{value}</p>
</div>
<div className={`text-4xl i-carbon-${icon}`}></div>
</div>
</div>
)
}
export default Widget
// src/components/Chart.tsx
import React from 'react'
const Chart: React.FC = () => {
return (
<div className="card h-64">
<h3 className="text-lg font-semibold mb-4">Sales Overview</h3>
<div className="h-48 bg-gray-200 flex items-end justify-around">
<div className="w-8 bg-primary" style={{ height: '60%' }}></div>
<div className="w-8 bg-primary" style={{ height: '80%' }}></div>
<div className="w-8 bg-primary" style={{ height: '40%' }}></div>
<div className="w-8 bg-primary" style={{ height: '70%' }}></div>
<div className="w-8 bg-primary" style={{ height: '50%' }}></div>
</div>
</div>
)
}
export default Chart最后,让我们在主应用程序中使用这些组件:
// src/App.tsx
import React from 'react'
import Header from './components/Header'
import Sidebar from './components/Sidebar'
import Widget from './components/Widget'
import Chart from './components/Chart'
const App: React.FC = () => {
return (
<div className="flex flex-col h-screen">
<Header />
<div className="flex flex-1 overflow-hidden">
<Sidebar />
<main className="flex-1 p-4 overflow-y-auto">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
<Widget title="Total Users" value={1234} icon="user" color="bg-primary" />
<Widget title="Revenue" value="$5,678" icon="currency-dollar" color="bg-secondary" />
<Widget title="Orders" value={90} icon="shopping-cart" color="bg-accent" />
<Widget title="Conversion Rate" value="2.3%" icon="chart-line" color="bg-gray-700" />
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
<Chart />
<div className="card">
<h3 className="text-lg font-semibold mb-4">Recent Activity</h3>
<ul className="space-y-2">
<li className="flex items-center">
<span className="i-carbon-user-avatar mr-2"></span>
<span>John Doe signed up</span>
</li>
<li className="flex items-center">
<span className="i-carbon-shopping-cart mr-2"></span>
<span>New order received</span>
</li>
<li className="flex items-center">
<span className="i-carbon-notification mr-2"></span>
<span>System update completed</span>
</li>
</ul>
</div>
</div>
</main>
</div>
</div>
)
}
export default App最后,更新 src/main.tsx 文件以包含 UnoCSS:
// src/main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import 'uno.css'
import '@unocss/reset/tailwind.css'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)现在,你可以运行项目:
bun run dev这个复杂案例展示了如何使用 UnoCSS 构建一个交互式仪表板。它包括以下 UnoCSS 特性:
- 使用预设(presets):Uno、Icons 和 Attributify
- 自定义主题颜色
- 使用快捷方式(shortcuts)定义复用样式
- 响应式设计
- 使用图标
- 组合多个实用工具类来创建复杂布局
这个例子展示了 UnoCSS 的强大功能和灵活性,同时也展示了如何在 Bun 环境中使用它来快速构建现代化的用户界面。
要使用 Bun 运行环境生成 UnoCSS 的复杂案例,以下是一个步骤详细的指南。这个案例将展示如何使用 UnoCSS 在 Bun 环境中创建一个具有多种功能的 UI 组件。
步骤 1: 初始化项目
-
安装 Bun:
curl -fsSL https://bun.sh/install | bash -
创建新项目:
bun init my-unocss-app cd my-unocss-app -
安装依赖:
bun add unocss @unocss/preset-uno @unocss/preset-attributify @unocss/preset-icons
步骤 2: 配置 UnoCSS
- 创建 UnoCSS 配置文件:
// uno.config.ts import { defineConfig, presetAttributify, presetIcons, presetUno } from 'unocss' export default defineConfig({ presets: [ presetUno(), presetAttributify(), presetIcons({ scale: 1.2, }), ], rules: [ ['m-1', { margin: '1px' }], ['m-2', { margin: '2px' }], ['text-lg', { fontSize: '18px' }], // 添加更多自定义规则根据需要 ], shortcuts: [ ['btn', 'px-4 py-2 rounded-md bg-blue-500 text-white cursor-pointer'], ['btn-blue', 'btn bg-blue-500 hover:bg-blue-700'], ['btn-red', 'btn bg-red-500 hover:bg-red-700'], ], theme: { colors: { primary: '#3b82f6', secondary: '#6b7280', }, }, })
步骤 3: 创建 React 组件
-
安装 React:
bun add react react-dom -
创建组件:
// src/components/Button.tsx import React from 'react' export const Button = ({ children, variant = 'blue', ...props }: { children: React.ReactNode; variant?: 'blue' | 'red' }) => { return ( <button className={`btn-${variant} ${props.className || ''}`} {...props} > {children} </button> ) } -
创建列表组件:
// src/components/List.tsx import React from 'react' export const List = ({ items, ...props }: { items: string[] }) => { return ( <ul {...props} className="list-none p-0 m-0"> {items.map((item, index) => ( <li key={index} className="m-2 text-lg"> {item} </li> ))} </ul> ) }
步骤 4: 使用组件
-
创建应用入口:
// src/App.tsx import React from 'react' import { Button } from './components/Button' import { List } from './components/List' const App = () => { return ( <div className="flex flex-col items-center justify-center h-screen p-4"> <h1 className="text-3xl mb-4">UnoCSS Demo</h1> <Button variant="blue" onClick={() => alert('Blue button clicked')}> Blue Button </Button> <Button variant="red" className="mt-4" onClick={() => alert('Red button clicked')}> Red Button </Button> <List items={['Item 1', 'Item 2', 'Item 3']} className="mt-8" /> </div> ) } export default App -
运行应用:
bun run src/App.tsx
总结
通过这些步骤,你可以在 Bun 环境中使用 UnoCSS 创建一个具有多种功能的 UI 组件。这个示例展示了如何定义自定义 CSS 规则、使用预设、并应用这些规则到 React 组件中.
参考资源
- UnoCSS 文档:了解更多关于 UnoCSS 的配置和使用方法.
- Bun 文档:了解更多关于 Bun 环境的配置和使用方法.
- React 文档:了解更多关于 React 组件的配置和使用方法.
这个案例是一个基本的示例,你可以根据需要添加更多功能和样式。 UnoCSS 的灵活性和可扩展性使得它非常适合用于构建复杂的 UI 项目。