当然可以。我们可以使用 UnoCSS 来实现主题和布局的自定义功能,并重构项目以支持模板功能。这将大大提高用户界面的灵活性和可定制性。让我们来重构这个项目:

  1. 首先,安装必要的依赖:
bun add unocss @unocss/preset-uno @unocss/preset-attributify @unocss/preset-typography
  1. 创建 UnoCSS 配置文件

在项目根目录创建 uno.config.ts

import { defineConfig } from 'unocss'
import presetUno from '@unocss/preset-uno'
import presetAttributify from '@unocss/preset-attributify'
import presetTypography from '@unocss/preset-typography'
 
export default defineConfig({
  presets: [
    presetUno(),
    presetAttributify(),
    presetTypography(),
  ],
  theme: {
    colors: {
      primary: {
        50: '#f0f9ff',
        100: '#e0f2fe',
        // ... 其他色阶
        900: '#0c4a6e',
      },
      // 可以添加其他颜色
    },
  },
})
  1. 更新 src/index.ts 以集成 UnoCSS
import { Elysia } from 'elysia'
import { html } from '@elysiajs/html'
import { staticPlugin } from '@elysiajs/static'
import { createGenerator } from '@unocss/core'
import presetUno from '@unocss/preset-uno'
import presetAttributify from '@unocss/preset-attributify'
import presetTypography from '@unocss/preset-typography'
 
// ... 其他导入
 
const uno = createGenerator({
  presets: [
    presetUno(),
    presetAttributify(),
    presetTypography(),
  ],
})
 
const app = new Elysia()
  .use(html())
  .use(staticPlugin())
  // ... 其他中间件
 
  .get('/uno.css', async () => {
    const { css } = await uno.generate('')
    return new Response(css, {
      headers: {
        'Content-Type': 'text/css',
      },
    })
  })
 
  // ... 其他路由
 
  .listen(3000)
  1. 创建主题和布局系统

创建 src/themes 目录,并添加一些主题文件:

/src
  /themes
    default.ts
    dark.ts
    colorful.ts

例如,default.ts 可能如下所示:

export default {
  name: 'Default',
  colors: {
    background: 'bg-white',
    text: 'text-gray-800',
    primary: 'text-blue-600',
    secondary: 'text-gray-600',
  },
  layout: {
    container: 'max-w-4xl mx-auto px-4',
    header: 'py-6',
    main: 'py-8',
    footer: 'py-6 text-center',
  },
}
  1. 更新 React 组件以使用主题

更新 src/components/Blog.tsx

import React from 'react'
import { Theme } from '../types'
 
interface BlogProps {
  posts: Post[]
  user: User | null
  theme: Theme
}
 
export const Blog: React.FC<BlogProps> = ({ posts, user, theme }) => {
  return (
    <div className={`${theme.colors.background} ${theme.colors.text}`}>
      <header className={theme.layout.header}>
        <div className={theme.layout.container}>
          <h1 className={`text-3xl font-bold ${theme.colors.primary}`}>My Blog</h1>
          <nav className="mt-4">
            <a href="/" className={`mr-4 ${theme.colors.primary}`}>Home</a>
            <a href="/stats" className={`mr-4 ${theme.colors.primary}`}>Statistics</a>
            {user ? (
              <span>Welcome, {user.name}! <a href="/logout" className={theme.colors.secondary}>Logout</a></span>
            ) : (
              <a href="/login" className={theme.colors.primary}>Login</a>
            )}
          </nav>
        </div>
      </header>
      <main className={`${theme.layout.main} ${theme.layout.container}`}>
        {/* 文章列表和其他内容 */}
      </main>
      <footer className={`${theme.layout.footer} ${theme.colors.secondary}`}>
        <div className={theme.layout.container}>
          &copy; 2023 My Blog. All rights reserved.
        </div>
      </footer>
    </div>
  )
}
  1. 实现主题选择功能

创建 src/components/ThemeSelector.tsx

import React from 'react'
import { Theme } from '../types'
 
interface ThemeSelectorProps {
  themes: Theme[]
  currentTheme: string
  onThemeChange: (themeName: string) => void
}
 
export const ThemeSelector: React.FC<ThemeSelectorProps> = ({ themes, currentTheme, onThemeChange }) => {
  return (
    <select
      value={currentTheme}
      onChange={(e) => onThemeChange(e.target.value)}
      className="p-2 border rounded"
    >
      {themes.map((theme) => (
        <option key={theme.name} value={theme.name}>
          {theme.name}
        </option>
      ))}
    </select>
  )
}
  1. 更新 src/index.ts 以支持主题切换
import { Theme } from './types'
import defaultTheme from './themes/default'
import darkTheme from './themes/dark'
import colorfulTheme from './themes/colorful'
 
const themes: Record<string, Theme> = {
  default: defaultTheme,
  dark: darkTheme,
  colorful: colorfulTheme,
}
 
app.get('/', async ({ cookie, html, set }) => {
  const currentTheme = cookie.theme || 'default'
  const theme = themes[currentTheme]
 
  const blogHtml = renderToString(
    <Blog posts={posts} user={user} theme={theme} />
  )
 
  return html(`
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>My Blog</title>
        <link rel="stylesheet" href="/uno.css">
        <script src="https://unpkg.com/htmx.org@1.9.0"></script>
      </head>
      <body>
        <div id="root">${blogHtml}</div>
        <script>
          function changeTheme(theme) {
            document.cookie = 'theme=' + theme + ';path=/';
            window.location.reload();
          }
        </script>
      </body>
    </html>
  `)
})
 
app.post('/change-theme', ({ body, set }) => {
  const { theme } = body as { theme: string }
  set.cookie('theme', theme, { path: '/' })
  set.redirect = '/'
})
  1. 添加主题选择器到 Blog 组件

更新 src/components/Blog.tsx

import React from 'react'
import { Theme } from '../types'
import { ThemeSelector } from './ThemeSelector'
 
interface BlogProps {
  posts: Post[]
  user: User | null
  theme: Theme
  availableThemes: string[]
}
 
export const Blog: React.FC<BlogProps> = ({ posts, user, theme, availableThemes }) => {
  return (
    <div className={`${theme.colors.background} ${theme.colors.text}`}>
      {/* ... 其他内容 */}
      <div className="mt-4">
        <form action="/change-theme" method="post">
          <ThemeSelector
            themes={availableThemes.map(t => ({ name: t }))}
            currentTheme={theme.name}
            onThemeChange={(themeName) => {
              const form = document.createElement('form')
              form.method = 'post'
              form.action = '/change-theme'
              const input = document.createElement('input')
              input.type = 'hidden'
              input.name = 'theme'
              input.value = themeName
              form.appendChild(input)
              document.body.appendChild(form)
              form.submit()
            }}
          />
        </form>
      </div>
      {/* ... 其他内容 */}
    </div>
  )
}
  1. 更新 Dockerfile 以包含 UnoCSS 生成

更新 Dockerfile:

# ... 前面的步骤保持不变
 
# 生成 UnoCSS
RUN bun run unocss
 
# ... 后面的步骤保持不变
  1. 更新 package.json 脚本
{
  "scripts": {
    "start": "bun run src/index.ts",
    "dev": "bun run --watch src/index.ts",
    "unocss": "unocss src/**/*.tsx -o public/uno.css"
  }
}

这个重构后的项目现在支持以下新功能:

  1. 使用 UnoCSS 进行样式管理,提供更灵活的主题定制能力。
  2. 实现了主题系统,允许用户在不同的预定义主题之间切换。
  3. 布局现在是可配置的,可以为不同的主题定义不同的布局。
  4. 主题选择器允许用户轻松切换主题。

注意事项:

  1. 确保在开发过程中运行 bun run unocss 来生成最新的 CSS 文件。
  2. 在添加新的 UI 组件时,记得使用主题中定义的类名和颜色。
  3. 考虑添加自定义主题功能,允许用户创建和保存自己的主题。
  4. 可以进一步优化性能,例如使用服务端渲染 (SSR) 或增量静态再生成 (ISR)。
  5. 考虑添加动画和过渡效果以增强用户体验。

这个重构提供了一个更加灵活和可定制的用户界面,同时保持了原有的功能。你可以基于这个基础继续扩展,添加更多的主题选项,或者实现更高级的布局定制功能。