news 2026/10/28 14:41:57

T3 Stack 实战:用 Next.js+tRPC+Prisma 打造全栈类型安全模板

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
T3 Stack 实战:用 Next.js+tRPC+Prisma 打造全栈类型安全模板

如果你最近在全栈开发圈里逛,大概率见过“T3”这个缩写——就是 t3.gg 那位社区大佬推的技术组合,通常由 Next.js、TypeScript、tRPC、Tailwind CSS 组成,再按需加入 Prisma 和 NextAuth。我拿这套组合做了一个自己的项目,名字就叫 t3code,初衷很简单:想给自己搭一套带认证、带数据库、带完整类型推导的全栈起点模板,以后做小工具、中后台甚至外包项目,可以直接拿来开局。结果从脚手架、数据建模、鉴权到部署,一路踩了无数坑。这篇文章把整个过程和背后的取舍逻辑写下来,适合两类人看:一是听说过 T3 Stack 但不知道值不值得上手的人,二是已经跑通了官方 demo、想在真实项目里把它用扎实的人。

1. t3code 到底是什么:从一个“名字误会”到我为什么需要它

1.1 先澄清:T3 不是三种技术的缩写那么死板

很多人第一次看到 T3 Stack,会下意识把它拆成 TypeScript、Tailwind、tRPC——因为它们都带 T。这个解释基本靠谱,但更准确的来源是 t3.gg 这个社区代号,项目作者是 Theo。他最开始其实是半开玩笑地做了个create-t3-app脚手架,想告诉大家“不要再纠结技术选型了,用这套组合写全栈很爽”。

所以,T3 Stack 实际上是一个约定俗成的组合拳:

组成负责什么为什么是它
Next.js前端框架 + SSR + API 层React 生态成熟,前后端一个项目维护
TypeScript全链路类型语言为 tRPC 端到端类型安全打基础
tRPC前后端接口调用免去手写 API 类型和请求代码
Tailwind CSS样式方案原子化类名,写界面速度快
PrismaORM 和数据建模Schema 直观,Migration 方便
NextAuth第三方登录与会话和 Next.js 配合最自然

其中 Prisma 和 NextAuth 是可选项。官方推荐“按需加入”,这一点很重要——很多人上手就把所有模块全勾上,结果项目被自己根本用不到的东西拖住。

1.2 我的 t3code 具体想解决什么问题

我给这个项目取名t3code,目标很明确:做一个“能直接拿去接需求”的起始仓库,而不是又一个官方 demo。我平时会接一些中小型项目,比如内部工具、内容管理后台、个人博客系统,这些项目的共同点是:都有用户登录、都有数据表、都有增删改查界面。如果每个项目都从零搭一遍鉴权和 CRUD,纯属浪费时间。

t3code 项目的核心约定:

  • 用户认证开箱即用,GitHub 和邮箱登录都配好
  • 数据层用 Prisma,预置 User、Account、Session 等基础模型,并留好扩展位
  • 所有业务接口通过 tRPC 暴露,前端调用时自动获得类型提示
  • 页面基础布局、暗色模式、通用列表页都先搭好骨架
  • 部署配置写在文档里,包括环境变量清单和常见报错

这个定位决定了后面所有技术决策的方向:宁可少做炫技,也要保证每一块都能被读者或客户直接复现。

1.3 技术选型时我做过什么对比

在定稿之前,我其实在几个组合之间犹豫过:

第一个候选是Next.js + REST API + react-query。这是最稳妥的路线,但问题在于前后端类型要维护两份,改一个接口字段要同步更新接口文档、类型定义和前端调用,烦得很。

第二个候选是Vite + Express + 前后端分离。这种方案的部署更灵活,但多一个项目、多一套部署流程,对小规模项目来说是负担。

第三个候选是tRPC。它最吸引我的地方在于:接口就是函数调用,类型从数据库模型一路推到前端按钮,中间没有任何代码生成步骤。改动数据模型后,前端所有相关页面直接变成红色报错,这种“改错地方立刻知道”的体验,是 REST 给不了的。

所以 t3code 最终落在 T3 Stack 上。它不见得是每个场景的最优解,但在“一个代码库、全类型安全、快速交付”这个目标下,它是效率最均衡的选择。

2. 脚手架实操:create-t3-app 没有替你解决所有麻烦

2.1 创建项目时的模块取舍

创建命令很简单:

npm create t3-app@latest t3code

交互式命令行会问你要启用哪些模块。我最终的选择是:Next.js、TypeScript、tRPC、Tailwind、Prisma、NextAuth,没用 Drizzle,也没用 eslint 之外的额外检查。取舍逻辑如下:

  • 我不是不喜欢 Drizzle,但 Prisma 的schema.prisma+ migration 流程对团队协作更友好,而且很多客户的后端同事只看 SQL,Prisma 生成的迁移文件更直观。
  • ESLint 只保留基础的next/core-web-vitals和typescript规则,不额外上 strict 全家桶。前端项目 lint 规则太多会降低交付速度。
  • 没有启用src/目录之外的额外选项,保持官方默认结构,方便新手查文档。

2.2 版本兼容性:容易被忽略的 Node 和包管理器

这里有个非常实际的坑:create-t3-app在不同时间生成的模板,依赖的 Next.js 版本差异很大。我用的时候默认装的是 Next.js 15,同时要求 Node.js 18.18 以上。

如果你同时装了很多 Node 环境,建议先用下面命令确认当前版本:

node -v corepack enable pnpm --version

我建议用 pnpm 而不是 npm,原因有两个:

  • pnpm 的依赖安装速度更快,磁盘占用更少,特别是 Next.js 这种依赖树庞大的项目
  • pnpm 的严格依赖隔离能暴露隐式依赖问题,反而帮你提前发现代码里“根本没写依赖却直接用”的情况

2.3 生成后的目录结构,哪些文件是你真正要改的

生成完的模板结构很清晰,但新手容易迷路。我认为最主要关注这几个文件:

src/ ├── app/ # Next.js App Router 页面 ├── server/ │ ├── api/ │ │ ├── routers/ # 你的业务接口都放这里 │ │ ├── root.ts # 合并所有 router 的入口 │ │ └── trpc.ts # tRPC 上下文和中间件定义 │ ├── auth.ts # NextAuth 配置 │ └── db.ts # Prisma 客户端实例 ├── trpc/ │ ├── server.ts # 服务端 tRPC 类型 │ └── client.ts # 客户端 React Query 封装 └── env.js # 环境变量校验

env.js是很多人容易忽略的点。它用 zod 做运行时校验,如果环境变量缺失或类型不对,启动时会直接报错。这其实是个好设计,但也会让首次启动直接卡住——比如没配好DATABASE_URL和NEXTAUTH_SECRET,连next dev都起不来。不要觉得这是 bug,这是模板在帮你尽早发现配置问题。

2.4 第一个真正的开发坑:Prisma generate 发生在什么时候

跑通pnpm dev之前,必须确保数据库连接没问题。我的步骤是:

# 1. 复制环境变量模板 cp .env.example .env # 2. 修改 DATABASE_URL 指向本地 PostgreSQL # 3. 执行迁移 pnpm prisma migrate dev # 4. 生成 Prisma Client pnpm prisma generate # 5. 启动 pnpm dev

我第一次没执行prisma generate就直接启动,结果db.ts里导入的PrismaClient一直报模块不存在。原因是新版 Prisma Client 默认输出到node_modules/.prisma/client,必须执行 generate 才会生成。另外,如果你的数据库还没有创建对应的 database,prisma migrate dev会直接报连接错误,不会自动建库。我的建议是先用createdb t3code把库建好,再跑 migrate。

3. tRPC 这把“双刃剑”:端到端类型安全的爽与痛

3.1 先说清楚 tRPC 的原理

很多第一次接触 tRPC 的人会问:它跟 REST 有什么本质区别?没有 URL、没有 GET/POST,前端怎么知道调哪个接口?

tRPC 的核心思路是:过程调用(Procedure Call)。你在服务端定义一个函数,客户端通过导入类型拿到这个函数对应的调用方式,然后像调用本地函数一样调用它。整个链路没有手写 HTTP 请求,也没有 GraphQL 那样的 schema 解析层。

看一个最简单定义:

// src/server/api/routers/post.ts import { z } from "zod"; import { createTRPCRouter, publicProcedure } from "../trpc"; export const postRouter = createTRPCRouter({ list: publicProcedure.query(async ({ ctx }) => { return ctx.db.post.findMany({ orderBy: { createdAt: "desc" }, take: 20, }); }), create: publicProcedure .input(z.object({ title: z.string(), content: z.string() })) .mutation(async ({ ctx, input }) => { return ctx.db.post.create({ data: input }); }), });

前端这样调用:

const utils = api.useUtils(); const createPost = api.post.create.useMutation({ onSuccess: () => utils.post.list.invalidate(), }); createPost.mutate({ title: "你好", content: "t3code" });

看到这样的调用,你就会明白为什么说“类型安全”是 tRPC 最大的卖点:title和content的类型、必填性、返回数据的结构,全部由服务端定义自动推导到前端。数据库改字段,前端立刻红一片,这是最直接的好处。

3.2 我在 t3code 里的实际做法:把路由拆成“资源”

项目规模一大,把所有接口堆在一个root.ts里会很难维护。我按领域拆 router:

// root.ts import { createTRPCRouter } from "../trpc"; import { postRouter } from "./post"; import { userRouter } from "./user"; import { projectRouter } from "./project"; export const appRouter = createTRPCRouter({ post: postRouter, user: userRouter, project: projectRouter, }); export type AppRouter = typeof appRouter;

每个 router 内部再拆 query 和 mutation:

  • query:一切读操作,包括列表、详情、下拉选项
  • mutation:一切写操作,包括创建、更新、删除、批量操作

这里有一个经验:写操作不要偷懒只写一个update。比如“发布文章”和“保存草稿”在业务上是两个语义,在代码里也应该是两个 mutation,哪怕底层都是更新同一张表。这样做的原因是,前端可以做更精准的缓存失效和错误处理,而不是每次刷新整张列表。

3.3 权限控制:publicProcedure 和 protectedProcedure

t3code 的核心需求之一是认证,所以绝大部分业务接口必须是“登录才能调用”。模板自带的protectedProcedure正好解决这个问题:

import { createTRPCRouter, protectedProcedure } from "../trpc"; export const projectRouter = createTRPCRouter({ create: protectedProcedure .input(z.object({ name: z.string() })) .mutation(async ({ ctx, input }) => { return ctx.db.project.create({ data: { name: input.name, ownerId: ctx.session.user.id, }, }); }), });

protectedProcedure会在没有合法 session 时直接抛错,前端收到的是未认证错误,根本不会执行数据库操作。这里面藏着 tRPC 的一个重要特性:校验在服务端执行,不是在中间件里靠 if 判断。所以不要在调用处再去检查登录状态,那既绕又容易漏。

3.4 我踩过的类型错误:input 类型没有显式导出

有一个非常隐蔽的坑:如果你在某个 mutation 里定义了复杂的 input schema,并且想在另一个文件里复用这个类型,直接import { $input }是拿不到的。tRPC 的类型推导是在内部完成的,外部能拿到的是整个 procedure 的类型。需要单独导出时,要显式定义一个类型:

const createProjectSchema = z.object({ name: z.string().min(1), description: z.string().optional(), }); export type CreateProjectInput = z.infer<typeof createProjectSchema>;

这样前端某个表单组件里才能做类似useForm<CreateProjectInput>的操作。我在 t3code 里把所有 zod schema 集中放在src/server/schemas/目录,router 和前端都从那里导入,避免类型散落。

3.5 错误处理的细节:httpBatchLink 和 errorFormatter

tRPC 默认用httpBatchLink,它会尝试把多个并发请求合并成一个 HTTP 请求。这很高效,但也有副作用:请求报错时,你可能拿不到直观的 HTTP 状态码,而是一个封装后的 TRPCClientError。

我的做法是在服务端自定义 error formatter,把业务错误清楚地返回:

// trpc.ts export const createTRPCRouter = ({ procedure, ... }) => { // 相关配置,包括 errorFormatter };

如果某个操作是表单校验失败、数据冲突或未授权,建议用TRPCError显式抛出:

import { TRPCError } from "@trpc/server"; throw new TRPCError({ code: "UNAUTHORIZED", message: "请先登录", });

前端就能针对error.data.code做对应的用户提示,而不是把泛泛的“请求失败”抛给用户。踩过的坑是:一开始我把错误都堆在onError里统一 toast,结果输入校验错误和服务器错误混在一起,调试起来很难受。后来改成按错误码分类处理,清晰多了。

4. 数据建模与鉴权:Prisma + NextAuth 的正确组合姿势

4.1 schema 设计:先把模型画清楚,再谈功能

t3code 最初只有用户表和文章表,后来我加了“项目表”来管理多个独立业务场景。每次加表,我都强迫自己先回答三个问题:

  • 这张表的核心实体是什么?它的生命周期是什么?
  • 哪些字段是业务字段,哪些是冗余字段?
  • 访问权限边界在哪里?

Prisma schema 示例:

model User { id String @id @default(cuid()) name String? email String? @unique emailVerified DateTime? image String? accounts Account[] sessions Session[] projects Project[] posts Post[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Project { id String @id @default(cuid()) name String ownerId String owner User @relation(fields: [ownerId], references: [id]) members String[] // 简化版成员列表 posts Post[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Post { id String @id @default(cuid()) title String content String published Boolean @default(false) projectId String? authorId String author User @relation(fields: [authorId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }

这里面有两个设计经验值得展开:

第一,Project.members我用的是String[]存用户 ID 列表。这是偷懒的做法,如果做正式系统,应该建ProjectMember中间表,否则查某个用户的所有项目要全表扫描。但对于一个人维护的 t3code 项目,我选择“先跑起来”,等确实有需要再迁移成关联表。这体现了 T3 社区的一个精神:不要过度设计。

第二,User模型里那些accounts、sessions字段是 NextAuth 的 Prisma Adapter 自动关联关系要求的。如果你把@default(cuid())改成别的 ID 生成策略,或者把字段名改了,NextAuth 就会报错。所以这些字段是框架的地基,不能随便动。

4.2 NextAuth 配置中的常见翻车点

t3code 使用了 GitHub 登录,配置过程看似简单,实际坑不少。

首先,环境变量必须配好:

AUTH_GITHUB_ID=xxxx AUTH_GITHUB_SECRET=xxxx AUTH_SECRET=xxxx

新版 NextAuth 用的是AUTH_SECRET而不是旧的NEXTAUTH_SECRET,这也是很多老教程过时的原因。生成密钥用:

npx auth secret

其次,GitHub OAuth App 的Authorization callback URL要填对:

http://localhost:3000/api/auth/callback/github

部署后还要把线上域名加进去。我第一次部署时忘了加线上的 callback URL,上线后点击登录一直跳回首页,控制台也没报错。这个排查了很久才意识到是 OAuth 应用配置的问题,和代码无关。

4.3 让 tRPC 的上下文里始终有 session

t3code 里最关键的衔接就是把 NextAuth 的 session 注入到 tRPC 的ctx。模板里已经写了类似逻辑:

export const createTRPCContext = async (opts: { headers: Headers }) => { const session = await auth(); return { db, session, headers: opts.headers, }; };

这样每个 procedure 都能拿到ctx.session.user。但要注意:auth()在服务端组件里调用没问题,如果你在普通 API Route 里也想拿 session,需要明确调用auth()而不是getServerSession,新版 NextAuth v5 的 API 变化比较大。

登录后的用户信息还可以通过 tRPC 暴露给前端,比如导航栏显示头像,我就写了一个getMe的 query:

getMe: protectedProcedure.query(({ ctx }) => { return ctx.session.user; });

4.4 路由级保护:middleware 到底该不该用

很多人会想到用middleware.ts去保护所有页面路由。我实测下来的建议是:不要依赖 middleware 做业务权限判断,它的主要用途是简单的“未登录访问受限页面就重定向”。

原因很简单:middleware 在 Edge 环境运行,不能直接用 Prisma,所以没法做精细的数据库权限校验。真正的权限边界必须在 tRPC 的 procedure 里做。middleware 只是提升体验:未登录跳转登录页,已登录访问登录页则返回首页。

t3code 里我保留了一个很薄的 middleware,只做一件事:

// middleware.ts export { auth as middleware } from "@/server/auth"; export const config = { matcher: ["/dashboard/:path*"], };

务必注意 matcher 的写法,很多人在这里写错导致整个网站都被重定向。我的经验是:路径模式要精确,不要图省事写/,不然静态资源都被拦截。

5. 部署上线:t3code 从本地到稳定运行的全过程

5.1 环境变量清单:部署前先对着这份表格检查

部署和本地开发最大的区别是环境变量。我整理了一份 checklist,每次部署都对着检查:

变量本地线上说明
DATABASE_URL本地数据库云数据库或内网地址注意线上是否要求 SSL
AUTH_SECRET开发密钥重新生成用npx auth secret
AUTH_GITHUB_IDOAuth App ID线上 App ID线上 callback URL 要匹配
AUTH_GITHUB_SECRET对应密钥对应密钥和 ID 是成对的
ENVdevelopmentproductionPrisma 按此判断日志级别

我在本地用 SQLite 其实也行,但生产环境建议直接上 PostgreSQL。原因之一是并发写入和事务处理不同,另一个原因是 Prisma 对 PostgreSQL 的特性支持更完整,比如String[]字段、枚举类型、JSONB 等。

5.2 构建阶段最容易出的问题:Prisma 引擎找不到

我在自建服务器部署时遇到过一个经典问题:容器或裸机环境构建 Next.js 时,Prisma Client 引擎文件没被正确复制,导致运行时一直报PrismaClientInitializationError。

解决方法是检查 Dockerfile 或构建脚本中是否在prisma generate之后执行了next build,并确保构建阶段的node_modules被完整保留。对于 Docker 多阶段构建,推荐用node:20-slim作为运行镜像,同时安装openssl:

RUN apt-get update && apt-get install -y openssl ca-certificates

如果使用 Vercel 部署,则不需要担心这个问题,平台会自动处理 Prisma 引擎和连接池。但我建议给数据库加一个连接串参数:

DATABASE_URL=postgresql://user:pass@host:5432/db?connection_limit=5

Vercel 的 Serverless Function 会为每个并发实例建立数据库连接,如果连接数被数据库上限卡住,你会看到间歇性超时。限制连接数能有效缓解。

5.3 上线后我实际遇到的三类问题

第一类是 Next.js 的缓存导致页面不更新。App Router 默认会把静态渲染结果缓存很久,改动内容后不清缓存就会觉得“数据不对”。实际上该把动态页面标记为动态渲染:

// app/projects/[slug]/page.tsx export const dynamic = "force-dynamic";

第二类是 tRPC 请求在服务端触发 React Query 刷新,导致无限刷新循环。根本原因是我在服务端组件里调用了api.post.list.useQuery(),又没有正确设置ssr: false或缓存策略。tRPC + React Query 在 App Router 下的 SSR 行为要和客户端调用区分开,否则很容易出现“客户端水合时请求又发一次”的问题。

第三类是日志太少,出错时无从查起。我的做法是在所有 mutation 的关键路径加日志,不是用console.log乱打,而是级别分清楚:

console.info(`[t3code] user ${userId} created project ${projectId}`); console.error(`[t3code] delete project failed: ${error.message}`);

上线后直接看服务日志就能定位数据操作问题,不需要到处翻数据库。

5.4 健康检查与自动重启

自部署时,我建议至少给进程加一个健康检查脚本:

curl -f http://localhost:3000/api/health || exit 1

t3code 里我留了一个极简的健康检查路由,返回数据库连接状态。配合 pm2 的max_memory_restart配置,能保证进程异常时自动恢复。别小看这一步,我遇到过内存泄漏直接宕机、又没有拉起机制的情况,半夜接客户电话的滋味真的不想再来一次。

6. 从 t3code 做出来后,我更想说的几件事

6.1 tRPC 的“类型安全”是有边界的

很多人宣传 T3 Stack 时,会把“端到端类型安全”吹得神乎其神。我的真实体感是:它消除了接口层的类型漂移,但没有消除业务逻辑层的类型漂移。比如表单提交的字符串和数据库里的枚举值,还是需要 zod 做运行时校验。类型安全解决的是“编码时”的错误,运行时错误永远要靠防御式编程兜底。所以我在 t3code 里坚持所有外部输入都过 zod,不在 procedure 里裸用input做逻辑。

6.2 下一个版本我会怎么做

t3code 目前已经稳定用于我自己的两个小项目,但下一个版本我想做三件事:

第一,把Project.members从String[]迁到真正的多对多表,并用事务保证数据一致性。这是从“能跑”到“经得起多人用”的必经一步。

第二,加入软删除。现在删除Post是物理删除,实际操作中我吃过亏,误删数据没法恢复。下一步在每个需要审计的模型上加deletedAt字段,查询时默认过滤。

第三,做一个可视化的路由调试面板。tRPC 没有像 GraphQL Playground 那样的现成调试界面,排查接口问题要靠前端页面触发。我想给 t3code 加一个/api/debug工具页,能够列出所有 tRPC procedure,并允许在浏览器里直接调用带 mock session 的接口。这对团队成员上手项目帮助很大。

6.3 给准备抄作业的人一个最终清单

如果你也想照着 t3code 的思路搭项目,我建议按这个顺序来:

  1. 先跑通create-t3-app原版模板,只加 Prisma,不加 NextAuth,熟悉数据模型和 tRPC 基本用法
  2. 第二步加上 NextAuth,把注册登录跑通,并理解 session 如何进入 tRPC context
  3. 然后写第一个完整的业务模块:列表页、详情页、创建表单、删除按钮
  4. 最后才关注部署:先部署到 Vercel 体验 Serverless 的“零运维”,再根据需求迁移到自己的服务器

整个过程不要贪快,每一层都要把报错读透。我在实际使用中最深的体会是:T3 Stack 的学习曲线不在脚手架本身,而在于你愿不愿意花时间理解“类型是怎么流过去的”。一旦把这个想明白,后面所有增删改查都变得很顺,因为数据从数据库到页面按钮,整条链路的形状是清晰可感的。t3code 现在还在持续迭代,如果你也在折腾同一套技术栈,欢迎把你在 tRPC 错误处理或 NextAuth 配置里遇到的奇葩问题翻出来对比一下,你会发现我们踩的坑重叠度相当高。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 14:40:43

Selenium、Cypress与Playwright:现代Web自动化测试工具对比

1. 自动化测试工具的现状与挑战在软件测试领域&#xff0c;自动化测试已经成为保障产品质量的重要手段。作为老牌工具的Selenium&#xff0c;自2004年诞生以来一直是Web自动化测试的代名词。但随着现代Web应用的复杂化&#xff0c;Selenium的局限性也日益显现。我最近在为一个电…

作者头像 李华
网站建设 2026/10/28 14:40:12

SWMM水文模块原理与参数校准实战指南

简介&#xff1a;本资源为《SWMM用户参考手册-水文学》&#xff08;第I卷&#xff09;中文译本PDF&#xff0c;是城市雨水管理领域核心工具SWMMH模型的权威水文理论与操作指南&#xff0c;面向环境工程、市政给排水、城市规划等方向的工程师、科研人员及高校师生&#xff0c;解…

作者头像 李华
网站建设 2026/10/28 14:38:57

ACL访问控制列表:原理、配置与实战应用

1. ACL基础概念与核心价值访问控制列表&#xff08;Access Control List&#xff09;作为网络设备的基础安全机制&#xff0c;已经存在超过30年。我第一次接触ACL是在2005年配置Cisco 2600路由器时&#xff0c;当时为了阻止某个部门的员工访问财务服务器&#xff0c;短短几行AC…

作者头像 李华
网站建设 2026/10/28 14:38:27

理性与感性的辩证关系:智慧的本质与协同效应

1. 重新审视智慧的本质&#xff1a;理性与感性的辩证关系在当代社会的高速发展进程中&#xff0c;我们常常陷入一种认知误区——将理性思维奉为至高无上的智慧标准。这种观念在教育体系、职场评价和社会价值观中表现得尤为明显。数学成绩优于语文成绩的学生往往被认为"更聪…

作者头像 李华
网站建设 2026/10/28 14:37:22

T3 Stack实战:从TypeScript到tRPC的类型安全全栈开发指南

t3code这个标签&#xff0c;我第一次看到时还以为是什么“第三版代码库”&#xff0c;后来在技术社区里翻了一圈才弄明白&#xff0c;它指的其实是围绕T3 Stack建立起来的那套全栈TypeScript开发范式。T3 Stack不是一个单一框架&#xff0c;而是Next.js、TypeScript、Tailwind …

作者头像 李华
网站建设 2026/10/28 14:35:51

WSL2 + GPU 直通:从零搭建 PyTorch CUDA 深度学习环境

1. 为什么我最终把 AI 开发环境搬进了 WSL21.1 从双系统到 WSL2 的折腾史搞深度学习这几年&#xff0c;环境问题一直是最让人头疼的事。最早我是 Windows 和 Ubuntu 双系统&#xff0c;每次想跑个模型都得重启切换&#xff0c;写代码在 Windows 上&#xff0c;训练又得跑到 Lin…

作者头像 李华