已解除速率限制,全面放开。
CollectUICollectUI

Default Monorepo

在默认 monorepo 项目(Web 单端)中安装和使用 HeroUI Pro 组件库

本文由人工撰写(md),AI 优化(mdx)。

Better T Stack 创建一个默认 monorepo(直达 Builder)。

技术栈:

技术栈选择

上边的 Bun 是运行时,下边的 bun 是包管理器。(bun 即能做运行时,又能做包管理器)

另外,选择其他的栈也是可以的,只是安装选择和具体路径可能不同,下边以此栈为例。

一步到位

跳过手动安装的过程,直接使用基于当前示例的模板。

克隆模板

npx -y degit rhywonfeong/hp-default-monorepo-template my-better-t-app
cd my-better-t-app

degit 会克隆仓库,但丢弃远程的 git 历史。

确保已安装 p cli,然后在任意路径下执行:

p clone --degit rhywonfeong/hp-default-monorepo-template my-better-t-app

它会以 degit 模式克隆远程仓库,放到统一的项目目录下(方便管理),并用 IDE 打开(默认 cursor)。

安装依赖

bun install

运行 hpsetup

启动预览

bun dev

手动安装

CLI 创建

bun create better-t-stack@latest my-better-t-app --yes

默认配置的命令是最简洁的,稍作修改,就会出现很多 flag 选项。

验证运行(可选)

dev 预览一下,看是否正常。

bun dev

验证运行

没问题,关掉,然后继续。

暂存提交(可选)

建议提交一次,方便之后查看变更,如果改坏了也可以快速撤回。

git status

有一个初始提交(better-t-stack 自动执行),两个变更(一个自动生成的,一个暂时为空的本地 db)。

全部忽略:

.gitignore
# Custom Ignore
local.db
*.gen.ts

暂存提交:

git add -A && git commit -m "wip: checkpoint before next changes"
git add -A; git commit -m "wip: checkpoint before next changes"

安装 HeroUI Pro

运行 hpsetup

根目录 下运行 hpsetup:

hpsetup 支持 monorepo,可以直接在根目录运行,add、安装到子项目,不用 cd 进去,官方 CLI 不支持。

选择安装 HeroUI React Pro,然后安装所有预选的对等依赖。

也可以部分安装,因为不是每个引入的 pro 组件都需要这些对等依赖。但问题是,你不知道引入的 pro 组件需要什么对等依赖,而且这里边有一些是必须的。

hpsetup 交互式安装

这里的版本是从本地缓存中恢复了,与官方 CLI 行为一致。每个版本的 tarball 是固定的。

也可以自动化进行,适合 CI 场景(会自动识别,不用手动加 --auto flag)。

hpsetup 自动化安装

引入 Pro 样式

packages/ui/src/styles/globals.css
@import "tailwindcss";
@import "@heroui/styles";
@import "@heroui-pro/react/css";
引入 Pro 样式

引入 Pro 组件

apps/web/src/components/area-chart-demo.tsx
"use client";

import {Card} from "@heroui/react";

import { ChartTooltip } from "@heroui-pro/react/chart-tooltip";
import {AreaChart} from "@heroui-pro/react/area-chart";

const revenueData = [
  {month: "Jan", revenue: 4200},
  {month: "Feb", revenue: 5800},
  {month: "Mar", revenue: 4900},
  {month: "Apr", revenue: 7200},
  {month: "May", revenue: 6100},
  {month: "Jun", revenue: 8400},
  {month: "Jul", revenue: 7800},
  {month: "Aug", revenue: 9200},
  {month: "Sep", revenue: 8600},
  {month: "Oct", revenue: 10200},
  {month: "Nov", revenue: 9800},
  {month: "Dec", revenue: 11500},
];

export default function AreaChartDemo() {
  return (
    <Card className="w-full max-w-[520px] rounded-2xl">
      <Card.Header>
        <Card.Title className="text-base">Monthly Revenue</Card.Title>
      </Card.Header>
      <Card.Content>
        <AreaChart data={revenueData} height={200}>
          <defs>
            <linearGradient id="revenue-fill" x1="0" x2="0" y1="0" y2="1">
              <stop offset="0%" stopColor="var(--chart-3)" stopOpacity={0.2} />
              <stop offset="100%" stopColor="var(--chart-3)" stopOpacity={0.02} />
            </linearGradient>
          </defs>
          <AreaChart.Grid vertical={false} />
          <AreaChart.XAxis dataKey="month" tickMargin={8} />
          <AreaChart.YAxis tickFormatter={(v: number) => `$${(v / 1000).toFixed(0)}k`} width={40} />
          <AreaChart.Area
            dataKey="revenue"
            dot={false}
            fill="url(#revenue-fill)"
            name="Revenue"
            stroke="var(--chart-3)"
            strokeWidth={2}
            type="monotone"
          />
          <AreaChart.Tooltip
            content={({active, label, payload}) => {
              if (!active || !payload?.length) return null;

              return (
                <ChartTooltip>
                  <ChartTooltip.Header>{label}</ChartTooltip.Header>
                  {payload.map((entry) => (
                    <ChartTooltip.Item key={String(entry.dataKey)}>
                      <ChartTooltip.Indicator color={entry.color ?? entry.stroke} />
                      <ChartTooltip.Label>{entry.name}</ChartTooltip.Label>
                      <ChartTooltip.Value>
                        ${Number(entry.value).toLocaleString()}
                      </ChartTooltip.Value>
                    </ChartTooltip.Item>
                  ))}
                </ChartTooltip>
              );
            }}
          />
        </AreaChart>
      </Card.Content>
    </Card>
  );
}

注意上边的 import 路径用的是子路径(@heroui-pro/react/area-chart),而不是主入口(@heroui-pro/react)。如果从主入口导入,可能会触发依赖解析错误或打包异常。详见 使用子路径导入

在首页预览

apps/web/src/routes/index.tsx
import { createFileRoute } from "@tanstack/react-router";

import AreaChartDemo from "@/components/area-chart-demo";

export const Route = createFileRoute("/")({
  component: HomeComponent,
});

function HomeComponent() {
  return (
    <div className="container mx-auto max-w-3xl px-4 py-2">
      <div className="grid gap-6">
        <AreaChartDemo />
      </div>
    </div>
  );
}

启动开发服务器:

bun dev

组件渲染

移除 shadcn 样式

有渲染,无报错,但样式不太对。

老问题,better-t-stack 默认会创建使用 shadcn/ui 的 monorepo。所以我们相当于是在 shadcn/ui 中引入 heroui(v3),两套完全不同的 UI 组件库/框架,导致部分样式异常(heroui 的部分样式被 shadcn 覆盖)。

如此,我们可以彻底移除 shadcn,只用 heroui。

不建议混用,即使当前组件的样式能够修复(移除 shadcn 覆盖 heroui 的那部分样式),以后引入其他组件也可能会出问题。

先移除 globals.css 中的其他样式,看是否正常吧。

packages/ui/src/styles/globals.css
@import "tailwindcss";
@import "@heroui/styles";
@import "@heroui-pro/react/css";
@import "tw-animate-css";
@import "shadcn/tailwind.css";
@source "../../../apps/**/*.{ts,tsx}";
@source "../**/*.{ts,tsx}";
@custom-variant dark (&:is(.dark *));
:root { ... }
.dark { ... }
@theme inline { ... }
@layer base { ... }

正常渲染

正常渲染😎

当然,shadcn 并没有完全清除,具体清理内容,参见 shadcn 清理记录

如果想跳过清理过程,也可以直接使用现成的模板。由 p cli 基于当前示例发布。

模板预览

快速开始请看 一步到位

How is this guide?

Last updated on

On this page