资讯详情

T3 Stack实战:从零构建代码片段收藏与管理工具

📅 2026/10/28 23:24:18 | 华诺云谱 👁 阅读
T3 Stack实战:从零构建代码片段收藏与管理工具
t3code 这个名字一看就是冲着 T3 Stack 去的。T3 不是一个单独的框架而是全栈开发里越来越流行的一套组合Next.js、TypeScript、Tailwind CSS、tRPC再搭配 Prisma 这类数据层工具。t3code 是我用这套技术栈从零做完的一个代码片段收藏与管理工具核心就是把散落在各种聊天记录、本地笔记、Gist 里的代码统一收进一个地方支持标签、搜索、语法高亮和快速复制。这篇文章不打算泛泛聊概念我会从需求拆解、数据模型、tRPC 路由、代码高亮选型到部署上线把整个项目的思考过程和实操细节完整记录下来。无论你是第一次接触 T3 Stack还是已经写过敏捷全栈项目想找一个能落地的练手参考这篇都能给你一些直接能用的东西。1. 项目拆解t3code 想解决的痛点与设计方案1.1 为什么需要自己做一个代码片段工具做 t3code 之前我一直被一个很常见的问题折磨写的代码太多但真正要复用的时候永远找不到。本地笔记软件里夹着几十段随手粘贴的代码GitHub Gist 上也存了一部分微信文件传输助手里还有一堆突然想到的片段。等到项目里要用某个算法、某段正则或者某个工具的配置只能一个个地方翻效率极低。市面上的代码片段工具其实不少比如很多开发者会直接用 VSCode 的 Snippets或者用 Notion 建表格。但我的核心需求很具体第一必须支持标签和多维筛选比如想找React 性能优化相关的片段能按复合条件筛出来第二代码要高亮展示最好复制时能保留原始缩进第三要支持私有部署代码放在自己的服务器上比较安心第四输入要足够快最好能快捷键唤起。市面上的工具要么太笨重要么数据不在自己手里要么不支持精细的标签体系。综合评估下来自己做一个小而美的工具是性价比最高的方案。这个需求听起来简单但真正做起来涉及的东西相当多用户体系要不要做、权限怎么控制、搜索做到什么深度、代码编辑器集成到什么程度。一开始我就明确了两条原则不追求大而全只做自己日常真正会用到的东西不过度设计先跑通一个最小闭环后续再迭代。t3code 最初的 MVP 只包含片段增删改查、标签管理和简单的关键词搜索但正是这个清晰的范围让整个项目能在两三个周末内完成并且一直稳定运行到现在。1.2 T3 Stack 为什么适合这个场景选 T3 Stack 不是追热点而是这个组合恰好覆盖了 t3code 的全部技术需求。先看 Next.jst3code 需要服务端渲染来保证首屏速度也需要 API 的能力来支撑前后端交互Next.js 的 App Router 把这两件事放在同一个框架里不需要再单独搭一个后端服务。再看 TypeScript代码片段管理工具本身就是给开发者用的整个项目的数据结构、类型系统如果不严谨后面维护起来会非常痛苦。TypeScript 让前端、后端、数据库查询的模型保持一致改一个字段会立刻在编译期暴露所有受影响的代码。t3code 里最有意思的部分是 tRPC。传统前后端接口方案需要写 REST 路由、定义请求响应类型、维护 API 文档稍微复杂一点就会前后端类型不同步。tRPC 的思路是直接把服务端函数暴露给前端调用类型定义从服务端一路传到客户端不需要生成独立的类型包也不需要手写接口文档。当我修改了 snippet 查询函数的返回结构前端代码立刻会在编译阶段提示哪里用错了字段。这种端到端类型安全的体验用过一次就很难回去。Tailwind CSS 可能看起来只是一个样式工具但对 t3code 这种工具型应用来说价值很大它不限制你用组件库可以快速实现代码编辑器面板、标签气泡、列表视图这些自定义 UI。Prisma 则负责数据层用 schema 定义 Snippet 和 Tag 的关系迁移、查询都写得非常顺手。整套技术栈有一个共同特征——足够现代、类型安全、开发体验好正好匹配一个人快速做出一个高质量工具的目标。1.3 功能边界与整体架构t3code 的整体架构其实不复杂Next.js 作为应用入口服务端组件负责页面渲染客户端组件负责编辑器和搜索交互tRPC 承担所有数据查询和变更Prisma 连接 PostgreSQL 数据库认证部分用 NextAuth 接入 GitHub 登录。数据流向是单向的用户操作先到 Next.js 页面页面通过 tRPC 调用服务端函数服务端函数再通过 Prisma 操作数据库响应原路返回。从功能上看我把它分成三层。基础层是片段管理新增、编辑、删除、复制片段内容包括代码正文、编程语言标识、标题和描述这个不用多说。管理层是标签和搜索每个片段可以有多个标签标签本身独立维护搜索支持按标题、描述、代码内容三个维度进行也可以组合标签过滤。体验层是代码展示支持常见语言的高亮、主题切换、一键复制、行号显示这些细节决定了工具日常用起来是否顺手。我刻意没有做的功能也值得说一说没有做权限细分只分登录用户和匿名用户没有做片段分享链接因为自己用的场景用不上没有做 WebDAV 同步数据直接存在数据库里。这样做的原因是任何功能都有维护成本与其做一堆看起来有用的功能不如把核心链路打磨好用。如果后续真的需要分享功能再单独加也来得及。2. 核心功能与关键实现2.1 数据模型设计Snippet、Tag 与用户的关系数据模型是整个 t3code 的地基我花了不少时间在设计 Prisma schema 这一步。代码片段最基本的结构是 Snippet 模型包含 id、标题、代码内容、编程语言、创建时间、更新时间。标题和代码内容用字符串存储编程语言用默认值这样从编辑器粘贴代码时不用每次都手动选一次语言。标签模型看起来简单但关系处理容易踩坑。一个片段可以有多个标签一个标签下可以挂多个片段这是典型的多对多关系。Prisma 里我用了隐式多对多表没有在产品代码里直接操作关系表而是通过snippet.tags和tag.snippets访问关联数据。用隐式关系的好处是查询代码非常简洁生成迁移时自动帮你建好中间表避免了手写联表查询的繁琐。用户模型和片段之间是一对多关系一个用户拥有多个片段。这里我做了个实用性的选择——默认不提供匿名创建功能所有片段都必须关联到登录用户。虽然会增加登录环节的摩擦但换来的是数据隔离的清晰每个用户只能看到自己的片段不涉及复杂的权限判断查询 where 条件直接写userId: session.user.id就完事。schema 里面还有一个容易被忽视但很重要的细节数据库索引。刚开始我只给 Snippet 建了主键索引结果片段数量到几千条之后按标题搜索和按标签过滤明显变慢。后来给language、createdAt和Tag.name都加了索引大部分查询都能走索引体验差距非常大。Prisma schema 里定义索引的方式也简单model Snippet { id String id default(cuid()) title String content String db.Text language String default(plaintext) isPublic Boolean default(false) createdAt DateTime default(now()) updatedAt DateTime updatedAt userId String user User relation(fields: [userId], references: [id], onDelete: Cascade) tags Tag[] relation(SnippetTags) index([userId, createdAt]) index([language]) } model Tag { id String id default(cuid()) name String snippets Snippet[] relation(SnippetTags) unique([name]) }这里我特别说明一个设计取舍isPublic字段虽然是随手加上的但它是后续做分享链接功能的关键默认 false 也符合私有工具的定位。onDelete: Cascade表示用户注销时其所有片段会自动删除避免留下孤儿数据。这些看似无关紧要的细节在数据量变大后都会变成维护成本的一部分。2.2 tRPC 路由与类型安全实践t3code 的 API 层全部由 tRPC 承担如果你第一次接触 tRPC可以把它理解成像调用本地函数一样调用远程函数。不用写 REST URL不用手工维护接口文档函数定义在服务端客户端拿到的是带完整类型推断的调用对象。服务端的入口文件一般叫trpc.ts负责初始化 tRPC 并定义基础上下文。我的实现里上下文会从 Next.js 请求中解析 session并把当前登录用户塞进 context。这样一个程序可以区分登录状态并在每个 procedure 里读取用户信息。import { initTRPC, TRPCError } from trpc/server; import { type CreateNextContextOptions } from trpc/server/adapters/next; import { getServerSession } from next-auth; import { authOptions } from /pages/api/auth/[...nextauth]; export const createContext async (opts: CreateNextContextOptions) { const session await getServerSession(opts.req, opts.res, authOptions); return { user: session?.user ?? null }; }; const t initTRPC.contexttypeof createContext().create(); export const publicProcedure t.procedure; export const protectedProcedure t.procedure.use((opts) { if (!opts.ctx.user) { throw new TRPCError({ code: UNAUTHORIZED }); } return opts; });真正写业务接口时我会把所有和片段相关的逻辑拆成一个snippetRouter里面包含 list、create、update、delete、getById 五个 procedure。以创建片段为例输入用 zod 做运行时校验因为 tRPC 只保证类型静态一致数据到运行时还是要防一手。zod 的 schema 既能做校验又能推断出输入类型和 TypeScript 配合起来非常自然。列表查询是 t3code 最核心的接口因为它直接决定了搜索体验。我实现的listSnippets接受keyword、tagIds、language、cursor四个参数其中cursor用于分页。这里我没有用传统页数而是用基于游标的分页主要是为了应对多条搜索结果时的不稳定问题。具体到 Prisma 查询关键词会拆开匹配标题、描述和代码内容标签条件通过some: { id: { in: tagIds } }实现语言条件直接精确匹配。代码实现如下listPublic: protectedProcedure .input( z.object({ keyword: z.string().optional(), tagIds: z.array(z.string()).optional(), language: z.string().optional(), cursor: z.string().optional(), limit: z.number().min(1).max(50).default(10) }) ) .query(async ({ ctx, input }) { const where { userId: ctx.user.id, ...(input.language ? { language: input.language } : {}), ...(input.keyword ? { OR: [ { title: { contains: input.keyword, mode: insensitive } }, { content: { contains: input.keyword, mode: insensitive } } ] } : {}), ...(input.tagIds input.tagIds.length 0 ? { tags: { some: { id: { in: input.tagIds } } } } : {}) }; const snippets await ctx.prisma.snippet.findMany({ where, take: input.limit 1, cursor: input.cursor ? { id: input.cursor } : undefined, orderBy: { createdAt: desc }, include: { tags: true } }); let nextCursor: string | null null; if (snippets.length input.limit) { nextCursor snippets.pop()?.id ?? null; } return { snippets, nextCursor }; });这一段代码基本代表了 t3code 后端的主要模式zod 定义输入边界protectedProcedure 保证只有登录用户能访问Prisma where 动态拼接最后返回数据。每次改动输入输出结构前端的所有调用点都会被 TypeScript 自动检查这种安全感是 REST API 很难给的。2.3 代码高亮与编辑器选型代码片段工具的灵魂是代码展示高亮库选型直接影响用户体验。市面上的方案主要有三种Monaco Editor、CodeMirror、hljshighlight.js或 Shiki 这类的纯高亮库。Monaco 是 VSCode 的编辑器内核功能非常强大支持自动补全、多光标、错误提示但代价是体积巨大即使按需加载也需要几百 KB。对 t3code 来说用户主要场景是看代码、复制代码不是写代码所以 Monaco 属于严重杀鸡用牛刀直接排除。CodeMirror 6 模块化做得很好也支持编辑和语法高亮但配置复杂度略高如果只做展示还得包一层外壳。最终我选择了 Shiki它基于 TextMate 语法做高亮能够生成带精确类名的 HTML样式可以直接复用常见 VS Code 主题视觉效果和代码语义都很好。实际使用中我发现一个关键问题Shiki 如果同步调用会让服务端渲染变得很慢。比如一个包含几百行代码的片段每次访问页面都要重新做一次语法解析首屏时间完全不能看。解决办法是把高亮过程缓存起来利用 Next.js 的服务端渲染能力在请求完片段数据后直接把高亮后的 HTML 返回给客户端而不是在浏览器里再跑一遍高亮逻辑。再加上 Next.js 的静态缓存同一个片段的页面重复访问几乎是秒开。代码展示组件里还有一个很容易被忽略的细节复制按钮的状态反馈。t3code 里我用了navigator.clipboard.writeText配合一个copied状态点击后按钮文案短暂变为已复制然后再变回来。这个交互看起来简单但需要在useEffect里处理定时器清理否则快速连点会造成状态错乱。这些细节对工具型应用的实际体验影响很大。2.4 全文搜索与筛选的真实做法搜索是最容易被低估的功能。t3code 早期版本就是简单地在内存里遍历片段关键词用includes做模糊匹配。片段少的时候看不出问题几百条之后标签筛选一旦和关键词组合使用响应延迟就变得很难受。升级方案是用 PostgreSQL 的全文本搜索。Next.js 应用 Prisma PostgreSQL 的组合下不需要额外引入 Elasticsearch 这种重量级组件。我通过 Prisma 的$queryRaw写了一个带tsvector全文索引的查询把标题、描述、代码内容拼接成一个可搜索的文本向量再用websearch_to_tsquery解析用户输入。这个方案在几万条片段范围内响应时间都在几十毫秒完全够用。CREATE INDEX snippet_search_idx ON snippet USING GIN ( to_tsvector(english, title || || content) );但这里要注意PostgreSQL 全文检索对中文支持不友好默认的 english 分词会直接把中文句子切成单个字导致中文关键词搜索效果很差。t3code 的目标用户是开发者代码注释通常夹杂中英文所以我在前端做了一层处理——如果搜索关键词包含中文就退回到contains模糊查询英文关键词才走全文索引。这是一个务实的妥协方案既不引入额外的分词组件也能让常见场景保持流畅。标签筛选的实现相对简单但有个细节值得一提标签联动。当用户选择了某个标签后标签下拉里的其他选项应该根据当前片段集合动态调整。这个效果用 Prisma 查询做会有点绕我直接用 tRPC 暴露一个getTagsWithCount接口统计每个标签关联的片段数量前端通过checkbox展示数量筛选时只显示数量不为零的标签。用户看到的就是选了这个标签组合后还剩多少片段体验比静态标签列表好很多。3. 实操过程从初始化到跑通全流程3.1 初始化 Next.js 项目与 Tailwind 配置开始动手的第一步是用官方脚手架创建项目。我在空目录执行了npx create-next-applatest t3code --typescript --tailwind --eslint --app这里我强烈建议加上--app因为 Next.js 的 App Router 新项目结构更适合服务和客户端组件混合使用。模板生成完我会先清理默认页面只保留根布局、核心排版和响应式配置。Tailwind 在模板里已经就绪但需要根据自己的设计调色板改一下tailwind.config.ts比如代码片段卡片需要等宽字体、背景色和边框色的命名规范。同时我会把tsconfig.json的路径别名配置好默认已有/*后续所有导入都从/开始避免过长相对路径。这一步看着基础但对后面 t3code 这种多模块项目非常重要。路径混乱会让重构时改一个文件名就全校报错。接下来是安装核心依赖。我通常一次性装好全部会用到的包npm install trpc/server trpc/client trpc/react-query tanstack/react-query zod prisma prisma/client next-auth auth/prisma-adapter tanstack/react-query注意 tRPC v11 官方推荐配 tanstack/react-query负责客户端状态管理。NextAuth 用于 GitHub 登录Prisma 适配器让它直接复用 Prisma 的用户表。依赖多但装完就一劳永逸。3.2 配置 Prisma 与数据库迁移Prisma 的配置重点有两个数据库地址和 schema。我开发环境用了本地 PostgreSQL生产环境用的是线上数据库服务两者只差一个DATABASE_URL环境变量。环境变量文件.env一定是被.gitignore忽略的这个细节我会在部署环节再强调一次。写 schema 时我会有意识地按业务模块分组。t3code 的 schema 不算复杂只有三个主要模型User、Snippet、Tag。User 是 NextAuth 适配器自动关联的标准模型Snippet 和 Tag 之间是多对多关系。为了减少查询次数关系字段都配置了include级别的外键索引。定义好 schema 后执行npx prisma migrate dev --name init这个命令会做两件事生成真正的 SQL 迁移文件并且同步生成最新的 Prisma Client。开发模式下它会自动执行迁移数据库结构马上就位。如果中途调整了字段类型比如把content从String改成db.Text也要再跑一次迁移确保开发数据库和生产数据库结构完全一致。3.3 实现第一个 tRPC 接口tRPC 的路由注册路径和文件命名有一套推荐约定。我在项目里建了一个server/api目录里面分trpc.ts、root.ts、routers/。trpc.ts负责初始化实例和定义上下文routers/下每个业务模块一个文件root.ts把所有子路由聚合到一起。创建接口我采用了标准的 mutation 写法。输入用 zod 定义一个对象包含标题、代码、语言和标签 ID 数组。因为标签可能要在创建时一并处理我会先在connectOrCreate里把新标签批量建好再建立关联关系。用connectOrCreate是我非常喜欢的一个 Prisma 功能。它的意思是如果标签名已存在就通过唯一索引连接否则先创建再连接。这样前端不用先手动创建标签直接传标签名进来就好简化了交互流程。对应的代码大概是create: protectedProcedure .input( z.object({ title: z.string().min(1).max(100), content: z.string().min(1), language: z.string().default(plaintext), tagNames: z.array(z.string().min(1)).optional().default([]) }) ) .mutation(async ({ ctx, input }) { return ctx.prisma.snippet.create({ data: { title: input.title, content: input.content, language: input.language, userId: ctx.user.id, tags: { connectOrCreate: input.tagNames.map((name) ({ where: { name }, create: { name } })) } }, include: { tags: true } }); });前端调用也非常直觉。组件里直接生成一个 mutation提交时把表单数据传过去成功后刷新列表const utils trpc.useUtils(); const createSnippet trpc.snippet.create.useMutation({ onSuccess: () { utils.snippet.list.invalidate(); } });第一次完整跑通这个链路的时候最让我震撼的是前端代码能直接补全后端 mutation 返回的字段不需要在客户端重新定义任何接口类型。这种全栈类型连接就是 t3code 选择 T3 Stack 最直接的价值。3.4 前端页面与交互设计后端通了之后前端就是把片段管理这四个字变成具体页面。t3code 的布局极其简单左侧是筛选栏右侧是片段列表和编辑器。筛选栏包含搜索框、语言下拉框、标签多选列表。列表页用服务端组件直接读取 tRPC 查询结果编辑器则是一个客户端组件。编辑页的核心是代码输入区域。我这里故意没有用笨重的编辑器组件而是直接用一个textarea配合font-mono和合理的 tab 键行为。为什么这样选因为 t3code 的核心场景是保存已有代码而不是手写大量代码一个 textarea 足够满足需求。不过我做了一个增强体验的小功能Tab键插入两个空格或者缩进减少编辑体验的割裂感。这个实现只需在onKeyDown里操作插入文本代码量很少但感知很强。列表展示时每个片段卡片包含标题、语言徽章、标签列表、更新时间和复制按钮。我设置了点击卡片就进入详情页同时复制按钮的点击事件用stopPropagation阻止触发父级跳转。这个细节如果没有处理用户想复制时会先跳到详情页体验非常糟糕。详情页则是把整段代码高亮渲染出来加一个顶部信息栏。交互部分还要考虑空状态和加载状态。t3code 搜索无结果时我会显示没有找到匹配的片段和清除筛选按钮引导用户返回全量列表。第一次加载数据时显示骨架屏而不是一个转圈动画因为骨架屏可以保持页面布局稳定交互感知更快。3.5 部署上线与环境变量管理t3code 的部署走的是 Vercel 托管数据库的路线因为 Next.js 应用部署到 Vercel 几乎不用额外配置。导入仓库后Vercel 会自动识别框架把npm run build作为构建命令。我遇到的最大麻烦是环境变量管理本地.env和生产环境的环境变量必须严格区分尤其要注意DATABASE_URL、NEXTAUTH_SECRET、NEXTAUTH_URL、GitHub OAuth 的GITHUB_CLIENT_ID和GITHUB_CLIENT_SECRET。Vercel 的项目设置里有一个 Environment Variables 面板我建议把所有变量一次性配置好再触发部署否则首次构建很容易因为缺少DATABASE_URL或NEXTAUTH_SECRET而失败。NEXTAUTH_SECRET可以用openssl rand -base64 32生成不要随意编一个短字符串。GitHub OAuth 应用的回调地址要填写生产域名下的/api/auth/callback/github这个地址如果写错登录会一直转圈。部署之后还需要做一次数据库迁移。Vercel 的构建流程会在生产环境跑 Prisma Client 生成但不会自动执行迁移。我采用的方式是在本地执行npx prisma migrate deploy指向生产数据库或者在线的数据库管理面板里执行迁移文件。这一步顺序错了会直接导致线上应用能启动但所有查询报错最好在部署文档里明确写出步骤。4. 常见问题与排查技巧实录4.1 tRPC 类型报错为什么客户端看不到服务端方法开发 t3code 的过程中我浪费时间最多的就是 tRPC 的类型报错。典型场景是新增了一个 procedure 后前端trpc.snippet.xxx一直提示不存在或者返回类型没有更新。排查下来大部分原因是根路由root.ts没有把新子路由合并进去。tRPC 是静态类型系统服务端路由没有注册客户端类型自然感知不到。另一个隐蔽原因是 TypeScript 增量缓存。tsconfig.json里开了incremental或者.next/cache未清理时新文件可能不会立刻被类型系统识别。我的标准排查顺序是先确认routers/文件导出正常再确认root.ts的appRouter包含该 router最后重启next dev或执行tsc --noEmit检查整个项目类型。如果还不行删除.next和node_modules/.cache再试大概率问题解决。4.2 Prisma 字段变更导致数据丢失或迁移失败t3code 迭代过程中我把 Snippet 模型加过isPublic字段也调整过 Tag 的唯一约束期间遇到过一次迁移失败。原因是在开发环境里表中已有数据不满足新加的唯一约束。比如 Tag 表里已经有两条同名数据现在要给 name 加唯一索引迁移必然失败。准备做这种破坏性变更时我现在的习惯是先备份或者直接在本地开发库跑一次试迁移。生产环境中要谨慎使用prisma migrate reset因为它会清空所有数据。如果是改字段类型比如从String改成db.Text最好在迁移 SQL 里加上USING子句做隐式转换否则复杂场景下会不可预测地报错。这些经验和 t3code 本身无关但对任何使用 Prisma 的项目都有参考价值。4.3 代码高亮卡顿从按需加载到缓存早期版本我用 Shiki 做高亮时把所有常用语言都导入了结果首次加载一个 Java 代码片段要等两秒多。排查后发现 Shiki 每个语言都需要注册对应语法注册全部语言会显著增加初始化时间。这个问题的标准解是只注册常用语言集合比如typescript、javascript、bash、python、json、tsx。冷门语言可以按需动态注册但 t3code 的使用频率决定了少而精更好。另一个优化是让 Shiki 只做一次高亮然后把结果交给 React 组件渲染。虽然 t3code 是服务端渲染但如果把高亮放在客户端组件里每次重新挂载都会重复计算。我把高亮计算放到了服务端数据层返回给前端的数据就是一个已经带高亮 HTML 的字符串前端只用dangerouslySetInnerHTML渲染。副作用是需要小心 XSS 风险但 t3code 渲染的是用户自己保存的代码这个风险可控。4.4 数据存储容量和性能监控t3code 运行一段时间后数据库体积和查询性能是需要关注的。PostgreSQL 在数据量增至几万行时仍表现良好但如果不维护索引、不清理无效数据查询延迟会逐步上升。我给自己加了一个简单的定时任务定期清理超过 180 天且从未被访问的临时片段同时用EXPLAIN ANALYZE检查慢查询。这里我发现一个有意思的点很多人喜欢在代码里到处加日志但在 t3code 这种小工具里更有价值的是给每个 tRPC 请求加耗时统计。我会在protectedProcedure上通过 middleware 统一记录请求路径和耗时这样哪个接口变慢了第一时间就知道。写日志本身很简单t.procedure.use(async ({ path, type, next }) { const start Date.now(); const result await next(); const duration Date.now() - start; console.log(${type} ${path} took ${duration}ms); return result; });这个中间件对性能影响极小但让我在后期的优化中省了大量排查时间。4.5 自己运维的最终心得最后分享一个实际体会。t3code 做完之后我最大的收获不是技术栈本身而是理解了工具的边界有多重要。每次想加新功能的时候我都会问自己这个功能是为了让日常使用更顺畅还是只想证明我能实现它很多功能后来都没加但现有功能的打磨却帮我把使用频率稳定地保持了下来。具体到技术上如果你也想照着这个思路做一个类似的工具我的核心建议是先把最小的闭环跑通也就是输入代码、保存标签、搜索代码这一条链路然后在这个链路上追求极致体验包括高亮、复制、筛选速度最后再考虑加分享、同步这类加分项。T3 Stack 的优势恰恰在于它能让你用很小的代价把这个闭环做得非常完整而且全程类型安全改起来不慌。现在 t3code 已经变成我日常开发离不开的工具截图、代码片段、临时命令都可以快速存进去搜索响应基本都是毫秒级。如果你也被自己散落各处的代码折磨我建议你也试着用这套技术栈花两三个周末做一个自己的版本过程中学到的全栈知识可能比工具本身更有价值。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑