使用 Vite 8、React 19、Tailwind CSS v4、shadcn/ui、Biome、Vitest 和 Hono 构建全栈 TypeScript 应用,涵盖前端(Vite/Rolldown 构建 + 开发)...
TypeScript 前端开发是一项面向实际任务的技能,主要用于用于构建类型安全 TypeScript apps的一连串: Vite 8 (建设 + dev 服务器, 滚动动力), React 19. 2 与 React 编译器, TypeScript 6. 0 (s)。它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。
若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;
涉及批量任务时,还应保存进度,避免中断后重复操作。该技能适合用于一次性任务,也可以接入自动化工作流,与其他技能或上层代理配合完成更完整的业务链路;在组合使用时,应明确每一步的输入输出关系,并避免不同步骤之间出现参数冲突。
构建类型安全 TypeScript 应用的一体化技术栈:Vite 8(构建 + 开发服务器,基于 Rolldown)、React 19.2(搭配 React Compiler)、TypeScript 6.0(启用 strict 模式)、Tailwind CSS v4.3 + shadcn/ui(样式方案)、Biome 2.4(代码检查与格式化)、Vitest 4(测试框架)以及 Hono 4(后端/边缘 API 框架)。各组件经过协同设计,可无缝集成——本技能覆盖它们的集成方式,以及横跨多个工具的边界问题(sharp edges)。Hono 的 RPC 客户端(hc)直接将服务端类型共享给 React 前端,从而实现端到端类型安全,无需代码生成(codegen)。
下方内容为跨工具的共性层:当这些工具交汇时会触发的规则约束,以及一个可运行的端到端示例。每个工具还配有深度参考文档 —— 请按需查阅:
use()、Activity、useEffectEvent、文档元数据及 React Compiler。import defer、tsgo。data-slot 的组件编写、注册表(registries)、Radix 与 Base UI 对比。biome check、领域(domains)、类型感知 linting、GritQL、ESLint/Prettier 迁移指南。| 工具 | 版本 | 说明 |
|---|---|---|
| Vite | 8.1.2 | Rolldown 是唯一默认打包器 |
| @vitejs/plugin-react | 6.0.3 | v6 移除了内联 babel 选项 |
| React / react-dom | 19.2.7 | React Compiler 已稳定(1.0) |
| babel-plugin-react-compiler | 1.0.0 | 使用 --save-exact 精确锁定版本 |
| TypeScript | 6.0.3 | 最后一个基于 JS 实现的 TS 版本;TS 7.0(tsgo)当前为 RC 阶段 |
| Tailwind CSS | 4.3.2 | CSS 优先配置,无 JS 配置文件 |
| shadcn/ui CLI | 4.12.0 | create 是 init 的别名 |
| Biome | 2.5.2 | 单二进制程序,统一支持 lint + format + imports |
| Vitest | 4.1.9 | Vite 原生测试运行器;复用 vite.config |
| Hono | 4.12.27 | 符合 Web 标准的后端/边缘框架;无 v5 版本 |
这些规则之所以容易引发令人困惑的失败,正是因为它们恰好处于两个工具的交界处。单一工具的细节请参阅对应参考文档。
react() 置后当框架插件(如 TanStack Router/Start)生成路由或转换代码时,必须在 @vitejs/plugin-react 之前执行,以确保 React 的 Fast Refresh 转换能作用于最终输出。错误的顺序将导致路由生成失败和 HMR 中断。
plugins: [ tanstackStart(), // 或 tanstackRouter()(SPA 场景)——框架插件优先 tailwindcss(), react(), // React 插件在框架插件中排最后 ]
React Compiler 1.0 在构建时自动对组件、计算逻辑和回调进行 memoization。请直接编写普通组件,**不要**主动使用 useMemo/useCallback/memo。关键难点在于 Vite 集成层:@vitejs/plugin-react v6 移除了内联 babel 选项,因此旧写法 react({ babel: { plugins: [...] } }) 不再生效。编译器现在通过独立的 Babel 插件运行:
import react, { reactCompilerPreset } from '@vitejs/plugin-react'
import babel from '@rolldown/plugin-babel'
plugins: [react(), babel({ presets: [reactCompilerPreset()] })]
安装命令:pnpm add -D @rolldown/plugin-babel @babel/core babel-plugin-react-compiler @types/babel__core。
该变化也影响 Biome:useExhaustiveDependencies 无法识别编译器已接管依赖管理,因此大多数编译器用户会禁用此规则(详见 biome.md)。
tailwind.config.jsTailwind v4 通过 CSS 中的 @theme、@utility、@plugin 和 @source 指令完成全部配置。**切勿创建或查找 tailwind.config.js/.ts**。Vite 集成仅需 @tailwindcss/vite 插件(同样无需 PostCSS 配置)。若在 v4 项目中发现 tailwind.config.js,说明是遗留文件,请删除并将其配置值迁移至 CSS 中。完整细节见 tailwind.md。
// 兼容主题 + 暗色模式// 破坏主题一致性 —— 禁止使用同时禁止拼接类名(如
bg-${color}-500)—— Tailwind 扫描器仅识别完整的字面量字符串,动态类名将静默失效(不生成任何 CSS)。请使用完整类名字符串的映射表(lookup map)。TypeScript 6.0 更改了默认配置 —— 应顺势而为,而非对抗
TS 6.0 内置了许多过去需手动配置的选项:
strict和noUncheckedSideEffectImports现在默认启用,因此新 tsconfig 中应省略这两项。但有两个新增默认值若被忽略将导致构建失败:types默认为[](若使用 Node 全局变量,需显式添加"types": ["node"]),且module/target行为已变更(module默认为esnext,不再是nodenext)。baseUrl已弃用,请改用带前缀的paths。完整 tsconfig 示例及迁移说明详见 typescript.md。单一 Biome 命令,且
files.includes是唯一有效的包含键始终运行
biome check(或 CI 中使用biome ci)—— 它可在单次执行中完成格式化、linting 和导入整理,**切勿**拆分为独立的lint+format调用。而在 Biome 2.x 中,唯一合法的文件筛选键是files.includes(注意末尾的s);files.ignore/files.include/files.exclude均不存在,若出现将抛出Found an unknown key错误。如需排除文件,请使用取反语法:"includes": ["**", "!**/routeTree.gen.ts"]。更多细节见 biome.md。Hono RPC 将后端类型与 React 前端绑定 —— 必须保持同步
当 API 使用 Hono 时,React 应用通过
hc客户端与其通信,该客户端直接导入服务端导出的() typeof app类型。这个共享类型即为关键交界点:它仅在前后端均使用相同 Hono 版本,且双方tsconfig.json均设置"strict": true时才有效(版本或 strict 配置不一致将报错 “Type instantiation is excessively deep”)。此外还有两条易踩坑规则:处理器(handlers)必须显式指定状态码(如c.json(data, 200)),客户端才能推断响应类型;客户端调用的路由不得使用c.notFound()。随着路由数量增长,建议一次性编译客户端类型(hcWithType),以保障 IDE 响应速度。完整细节见 hono.md。端到端示例
一个最小但完整的 React + TypeScript + Tailwind + Biome 项目。你可以将框架插件替换为你选用的路由/SSR 方案(参见 vite.md 中关于 TanStack 和 Cloudflare 的变体说明)。
vite.config.ts
import { defineConfig } from 'vite' import react, { reactCompilerPreset } from '@vitejs/plugin-react' import babel from '@rolldown/plugin-babel' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ plugins: [ tailwindcss(), react(), babel({ presets: [reactCompilerPreset()] }), ], resolve: { alias: { '@': new URL('./src', import.meta.url).pathname }, }, })
import.meta.url是 ESM 规范下解析路径的正确方式 —— ESM 配置中不存在__dirname,且 Vite 配置文件强制要求为 ESM 格式。tsconfig.json(TypeScript 6.0)
{ "compilerOptions": { // strict + noUncheckedSideEffectImports 在 6.0 中默认开启 —— 故此处刻意省略 "target": "es2023", "module": "preserve", "moduleResolution": "bundler", "moduleDetection": "force", "jsx": "react-jsx", "verbatimModuleSyntax": true, "isolatedModules": true, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, "erasableSyntaxOnly": true, "skipLibCheck": true, "types": [], "paths": { "@/*": ["./src/*"] } } }
module: preserve+moduleResolution: bundler是 Vite 打包应用的推荐组合;仅当代码需在 Node 中直接执行时,才改用nodenext。types: []可防止全局污染式引入@types/*—— 有需要时再显式添加(如["node"])。biome.json
{ "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, "files": { "includes": ["**", "!**/components/ui", "!**/routeTree.gen.ts"] }, "formatter": { "enabled": true, "indentStyle": "space", "lineWidth": 100 }, "linter": { "enabled": true, "rules": { "preset": "recommended" }, "domains": { "react": "recommended" } }, "javascript": { "formatter": { "quoteStyle": "double" } }, "assist": { "enabled": true, "actions": { "source": { "organizeImports": "on" } } } }src/styles.css
@import "tailwindcss"; :root { --background: oklch(1 0 0); --foreground: oklch(0.145 0 0); --primary: oklch(0.205 0 0); --primary-foreground: oklch(0.985 0 0); --radius: 0.5rem; } .dark { --background: oklch(0.145 0 0); --foreground: oklch(0.985 0 0); --primary: oklch(0.922 0 0); --primary-foreground: oklch(0.205 0 0); } @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); }
@import "tailwindcss";这一行至关重要:@tailwindcss/vite插件本身不生成任何样式,缺失此行即导致经典的 “Tailwind 渲染为空” 问题。对引用 CSS 变量的标记(tokens),请使用@theme inline(非单纯的@theme),以确保其响应暗色模式切换。一个符合全栈规范的组件
纯函数组件、
ref作为普通 props(无需forwardRef)、通过React.ComponentProps继承原生元素属性、使用 CVA 实现变体(variants)、通过data-slot支持样式钩子(styling hooks)、不手动 memoize —— 编译器自动处理。import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const buttonVariants = cva( "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors disabled:opacity-50", { variants: { variant: { default: "bg-primary text-primary-foreground hover:bg-primary/90", outline: "border border-input bg-background hover:bg-accent", ghost: "hover:bg-accent hover:text-accent-foreground", }, size: { default: "h-9 px-4 py-2", sm: "h-8 px-3", lg: "h-10 px-8" }, }, defaultVariants: { variant: "default", size: "default" }, } ) function Button({ className, variant, size, ref, ...props }: React.ComponentProps<"button"> & VariantProps) { return ( ) } 注意
cn()的参数顺序:先传入默认类名,再传入用户传入的className,这样 tailwind-merge 的 “后覆盖”(last-wins)机制才能让调用方成功覆盖样式。最佳实践
- 让编译器负责优化。 直接编写普通组件和计算逻辑;仅在极少数需保证值作为稳定 effect 依赖的场景下,才使用
useMemo/useCallback。- 将状态建模为可区分联合类型(discriminated unions),而非松散布尔值(例如
{ status: "loading" } | { status: "error"; error }),使非法状态不可表达。- 使用
React.ComponentProps<"el">扩展原生 props,而非手动重新声明 HTML 属性。- 优先使用
use()而非useContext()—— 它支持在早期返回(early returns)和条件语句中工作。- 仅使用语义化颜色标记(semantic color tokens),且始终将
bg-*与匹配的text-*-foreground成对使用。biome check --write是本地开发唯一命令;CI 流水线中使用biome ci。- Rolldown 是 Vite 8 的默认打包器 —— 无需额外启用;使用 Rolldown 的
codeSplitting功能拆分稳定的 vendor 代码(详见 vite.md)。- 对重写代码的工具,精确锁定版本(如
babel-plugin-react-compiler、@biomejs/biome),避免版本升级带来意外差异。- 敏感信息严禁暴露至客户端 —— 仅
VITE_前缀的环境变量可通过import.meta.env注入浏览器代码。- 测试应基于
vite.config.ts—— Vitest 复用你的构建配置,因此测试能感知相同的别名和转换逻辑;CI 中运行vitest run,组件测试使用jsdom。资源
- Vite:https://vite.dev/guide/ — Vite 8 博客:https://vite.dev/blog/announcing-vite8
- React 19.2:https://react.dev/blog/2025/10/01/react-19-2 — Compiler:https://react.dev/learn/react-compiler
- TypeScript 6.0:https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/
- Tailwind CSS:https://tailwindcss.com/docs — shadcn/ui:https://ui.shadcn.com/docs
- Biome:https://biomejs.dev/