
t3code 是我这两年一直在维护的个人开发基建项目名字听起来像一个开源框架其实它特别朴素。当时我刚换到一台新笔记本重新配完环境之后发现自己每年都在重复同一套动作新建项目时手敲一堆文件写过的代码片段散落在十几个地方各种构建、提交的小脚本藏在不同的目录里等到要用的那天又找不到。我想了很久决定把这些东西收敛成一个代号为 t3code 的项目——它不是一个产品不是一套框架而是我给自己定的一套“开发日常标准化方案”。很多人问我t3code 到底是什么能做什么。用一句话回答它是一个以代码为中心的个人工作台包含模板、代码片段和脚本三块内容。名字里的 T3 和现在流行的 T3 技术栈没有关系我取的是三个 TTemplates模板、Snippets片段、Scripts脚本。这套东西适合谁适合那些已经被现代工具链包围却还是觉得自己在“重复造轮子”的人适合一个人维护多个小项目、经常需要从零初始化工程的独立开发者也适合想把团队里的公共脚手架、代码规范沉淀下来的初级技术负责人。1. t3code 是什么一次对抗“环境散乱”的自我重构1.1 名字里的三根支柱先说说 t3code 的骨架。所有东西都围绕代码这个中心按照它们的性质分到三个顶层目录templates、snippets、scripts。templates保存各种项目脚手架模板。以前我大概是全网最喜欢用官方脚手架初始化项目的人之一一个git clone加一堆手动修改就能开跑的项目我要么每次都上网找配置要么打开几个月前某个项目复制一遍。templates 目录把这类东西固化成可复用的模板。snippets是一个真正意义上的代码片段库。不放在 IDE 的配置文件里也不放在笔记软件里而是做成普通文本文件带上说明和管理脚本既可以在编辑器里补全也可以直接搜索、批量迁移。scripts是所有重复性操作的落点。比如初始化新项目前改名字、同步 IDE 片段、生成项目的 README这类“做一次很烦、做两次就腻、做三次必定出错”的活儿全部收敛成命令。这三块不是独立的。模板里会引用片段脚本会操作模板片段又会被脚本同步到编辑器。它们串起来的逻辑是从模板出发以片段补充业务以脚本完成动作。1.2 它到底解决了我哪三类问题以前最折磨我的三件事在 t3code 建立之后都消失了。第一类问题是新项目初始化靠记忆。同一个 Node 服务我可能隔两个月就新起一个每次都要回忆 package.json 里到底放哪些字段、tsconfig 的严格模式开关是什么、eslint 的规则集怎么配。这些信息本身不难找难在它们分散在各处且每次都要重新“组装”。第二类问题是代码片段的“丢档”。编辑器里的 snippet 配置是 JSON 格式写错了整个自动补全失效笔记软件里的代码块没法直接被编辑器读取网上的代码片段又各有各的习惯。我需要的是一种既方便人阅读、又方便程序解析的存法。第三类问题是小脚本的失控。以前每个项目里都躺着一个deploy.sh、一个build.sh名字可能一样内容千差万别。真正危险的是某次上线前我发现自己改错了脚本却连“改错之前的版本”都找不回来。这三类问题指向同一个本质我缺的不是工具而是管理工具的方式。t3code 就是顺着这个思路长出来的。下面是重构前后的对照可能比文字更直观场景重构前重构后新建一个 Node TS 项目手动复制旧项目再删掉一堆无关文件t3code apply node-ts my-app从旧电脑迁移环境导出/导入 IDE 配置手工重装插件执行sync-snippets把片段库同步过去一次搞定频繁重复的运维/构建操作各个项目里各写一套脚本统一存放版本管理跨项目复用2. 核心模块拆解templates / snippets / scripts 的设计逻辑2.1 templates一套“够用但不过度”的脚手架模板templates 这个模块我最怕它变成“大而全”。刚开始整理的时候我一度想做一个能把前端、后端、移动端、脚本工具全都塞进去的超级模板库。后来实际用了几次才发现模板的价值恰恰在于“克制”。我的每套模板都遵循同样的组成规则一个极小的核心文件集只包含项目跑起来必须的文件入口文件、配置文件、包管理清单。一个.t3code/config文件记录该模板的名称、描述、默认端口、需要的变量。一个README.tpl.md里面用占位符表示项目名和作者信息方便生成后直接替换。举个最小例子一个node-ts模板的目录结构大概是这样的templates/node-ts/ ├── src/ │ └── index.ts ├── package.json ├── tsconfig.json ├── .env.example └── .t3code/ └── config.json为什么如此精简因为模板真正要做的是“把固定的东西固定下来把变化的东西暴露成变量”。如果模板里塞了业务逻辑、塞了某个具体数据库的连接方式那它就不是模板而是一个需要拆解的历史包袱。实际创建项目时脚本读取.t3code/config询问几个必要信息然后在目标目录把文件名和内容中的占位符替换掉这个项目就能用了。2.2 snippets代码片段的知识管理协议片段库是整个 t3code 里复用率最高的部分。我一开始用的是编辑器自带的 JSON 格式但 JSON 对注释不友好也没有办法加太多上下文说明。后来我换了一种方式把每个片段写成一个独立的 Markdown 文件用 YAML front matter 保存元信息再用一段小脚本转换成 VS Code / 其他编辑器认识的格式。单个片段文件大概是这样的格式--- name: 新建 Vue3 组件 prefix: v3comp scope: vue,typescript tags: [vue3, 组件] --- 创建一个 Vue3 的 SFC 组件默认导出组件对象可以直接作为列表项组件使用。 ts script setup langts const props defineProps{ item: Item; }(); const emit defineEmits{ (e: select, id: number): void; }(); /script template div classitem-card slot / /div /template之所以坚持这种格式有几个实际原因 第一**片段必须带上下文**。代码片段如果只有一段代码三个月之后我自己都看不懂为什么要这么写。Markdown 的主体部分可以写这个片段的适用场景、依赖条件、注意事项。 第二**片段必须能被搜索**。tags 字段可以让我用关键词或者文件名直接 grep 出来。这在跨机器迁移时尤其有用因为编辑器里的 snippet 一旦脱离人能读的格式就是一份死数据。 第三**片段必须是“活”的**。脚本会在每次变更后读取整个目录生成一份合并后的 JSON再软链接到编辑器的配置目录。这样子目录里是给人看的源文件编辑器配置目录里是给机器用的产物源头永远只有一个。 ### 2.3 scripts把重复动作收敛到少数几个命令 scripts 目录的设计原则有三条幂等、可重放、单一入口。 “幂等”的意思是同一个命令跑两遍和跑一遍结果一样。比如 apply 脚本创建项目时如果目标目录已经存在就应该停下来报错而不是默默覆盖“可重放”指的是所有脚本不应该只针对当前机器的当前状态生效换一台新电脑clone 下 t3code 仓库后直接跑也能得到一样的结果“单一入口”则是我把所有核心操作都挂在一个 t3 命令后面不用记住十几个不同脚本的名字。 入口大概长这样 bash t3 list templates # 列出所有模板 t3 apply template name # 用模板创建项目 t3 sync snippets # 把片段库同步到编辑器 t3 run publish # 执行发布脚本每一段脚本都是独立的、小的、只干一件事的。我不会把它们拼成一个巨大的自动化流水线因为个人开发场景下的需求变化很快把太多步骤耦合在一起反而会让排错变得困难。t3code 更像是一个工具橱柜每把工具都挂在各自位置上需要时拿起来用而不是一台把所有工序卷在一起的机器。3. 把 t3code 落地目录设计、命令行与编辑器联动3.1 整个工程的目录布局t3code 本身也是一个普通的 Git 仓库顶层结构非常简单t3code/ ├── templates/ │ ├── node-ts/ │ └── vite-lib/ ├── snippets/ │ ├── ts.md │ ├── vue.md │ └── ast.md ├── scripts/ │ ├── bin/ │ │ ├── t3 │ │ ├── apply.js │ │ └── sync.js │ └── publish/ │ └── publish.sh └── README.md这样的布局最大的好处是可预测。不管是在公司电脑还是家里电脑我克隆下来之后扫一眼目录名就知道东西放在哪里。模板、片段、脚本三者互不污染新加内容时不需要动旧文件。我强烈建议如果你也打算搭一个类似项目先把目录结构定下来再开始往里填内容。因为内容会不断增长如果一开始结构就是乱的后面很难有动力整理。3.2 命令行入口写三个小脚本而不是一个大 CLI很多人在自建工具时最容易掉进去的坑就是想做成一个正经的 CLI 框架引入 commander、yargs、各种插件。实际上对于个人使用一个t3壳脚本加几个 Node 文件已经绰绰有余。我的实现非常简单根目录放一个名为t3的可执行脚本内容是 bash 的 case 分发#!/usr/bin/env bash case $1 in apply) node $(dirname $0)/scripts/bin/apply.js $2 $3 ;; sync) node $(dirname $0)/scripts/bin/sync.js $2 ;; run) bash $(dirname $0)/scripts/publish/$2.sh ;; *) echo Usage: t3 [apply|sync|run] ;; esac选择这种做法的理由是个人工具的需求变化频率很高用最少的抽象层改起来最快。等某一天真的需要面向多人使用再迁移到正式 CLI 也不迟但现在没有必要为一个个人项目引入额外的依赖和构建步骤。3.3 与编辑器的联动片段库不是“死文件”片段同步是 t3code 里我最满意的部分。VS Code 的 User Snippets 目录里只需要放一个软链接指向 t3code 生成的聚合 JSON 文件。这样一来我在项目里新写一个 Markdown 片段文件只要跑一次t3 sync snippets编辑器立刻就能用上自动补全。但这里有一个非常关键的细节软链接不能指向源目录目录本身因为 VS Code 要求 snippets 目录下的文件必须是普通文件或 JSON 格式而我的源文件是 Markdown。所以 sync 脚本的实际动作是读取 snippets 目录下所有 Markdown 的 front matter 和正文代码块组装成一个标准的 VS Code snippets JSON写到~/.config/Code/User/snippets/t3code.json。这个流程绕了一点路但换来的好处是可读性和可更新性。更新片段不用再小心翼翼地去数 JSON 大括号换机器时只需要同步整个 t3code 仓库再跑一次 sync所有编辑器配置就回来了。4. 我在 t3code 上踩过的坑以及绕过它们的方法4.1 模板变量转义模板文件里一启动全是空的t3code 最早期版本apply 脚本用的是简单粗暴的字符串替换把形如{{projectName}}的占位符直接替换成用户输入的值。听起来没问题直到我第一次用这个模板生成一个 TypeScript 项目。模板里的src/index.ts中并没有{{projectName}}但是有大量类似${...}的模板字符串。我用的替换函数本身没有正确处理这些内容导致生成后的文件里原本的模板字符串被错误解析变量全部变成 undefined。程序一运行控制台一片空白。排查的链路是这样的先对比生成前后的文件发现tsconfig和源码里所有${}后面的内容全部丢了然后定位到是我自己写的正则把${}当成了模板变量的边界最后我把占位符从双大括号改成了NAME、AUTHOR这种纯字母的写法并且在替换时完全不碰${}。事后总结两个教训模板引擎的处理逻辑要尽量简单越简单越不容易误伤占位符一定要选代码里几乎不可能出现的字符串。{{}}看着很正规但在某些语言里并非绝对安全。4.2 跨平台路径与换行的差异t3code 早期我主要在 macOS 上用写了很多基于 bash 的脚本以为到了 Linux 也一样没问题。直到一次出差在 Windows 上跑壳脚本直接挂掉路径分隔符、sed的换行处理、Shell 环境变量通通不一样。我没有选择把脚本全部重写成跨平台的编程语言而是做了一个比较实际的拆分需要处理文本和路径的部分全部用 Node.js 来做因为 Node 的path模块天然处理跨平台问题而真正操作系统层面的操作比如启动服务、设置权限则保留 bash 脚本并且在脚本开头做一次环境判断给出来自不同平台的提示。那次踩坑之后我养成了一个习惯任何脚本第一版写完后立刻在另一台不同操作系统的机器上跑一遍。问题出现得越早修复成本越低。4.3 文档与代码“两套事实”的同步问题t3code 里 README 更新是另一个让我头疼的点。新增一个模板或者片段之后我经常忘了更新 README导致过了几周README 上的列表和实际目录对不上。后来我写了一个小脚本每次执行t3 summary它会遍历templates/和snippets/下的所有文件自动生成目录索引并覆盖写入 README 的“项目清单”部分。这样 README 里除了那一段固定的项目简介外所有动态内容都是自动生成的永远不过时。这里真正要说的不是文档生成技术而是一个原则凡是能用程序生成的信息就不要记录在静态文档里。静态文档只保留人必须手写的解释性内容这能从根源上解决“文档漂移”。4.4 不要让 t3code 变成“第二份笔记软件”还有一个偏“心态”层面的坑。有一段时间我给 t3code 加了很多功能把收藏的网址也存进来把日记也存进来把临时工作日志也存进来。结果它变得既像笔记软件又像书签管理器还像代码编辑器。最后我发现一个工具一旦什么都装就什么都装不好。t3code 的核心是 code围绕代码的模板、片段、脚本这是它的边界。那些不属于代码的东西我后来清理出去了或者只保留了一个指向外部地址的链接。如果你也想建类似项目请一开始就想清楚边界否则后面整理成本会让你放弃这个项目。5. 想搭一套自己的 t3code我先给你三个建议5.1 先用两周记录痛点再动手搭工具很多人看到这类项目的第一反应是“我也要搞一个”然后立刻开始写脚本。实际干起来发现自己并没有那么多真实的痛点最后做着做着就停了。我建议你先在普通工作流程里记录两周把每次觉得“真烦”“又来了”的动作都记下来。两周之后你大概率会发现痛点集中在几个固定动作上。然后只需要围绕这几个动作建工具而不是凭想象建一个庞大的体系。5.2 保持“个人化”不要追求普适t3code 的好用是因为它完全按我的习惯设计。网络上有非常成熟的脚手架、snippet 管理工具和脚本框架它们优秀但通用通用意味着它们必须考虑所有人的需求必然有很多我不需要的分支。个人工具的优势就是“偏科”只服务自己所以可以大胆地按照自己的偏好做决策哪怕这个决策在别人看来不算最佳实践。5.3 注意敏感信息与合规边界这里必须提醒一句模板里千万不要放公司内部代码片段库里也不要存任何带敏感信息的配置。t3code 如果托管在公开平台那它暴露的所有内容都等于公开展示即使是私有仓库也最好从一开始就养成不提交密钥的习惯。我在.env.example里只留 key 名不留真实值所有可能有敏感数据的模板目录都会在 apply 之后自动生成.gitignore把真实环境文件排除在版本管理之外。这个习惯能帮你省掉不少麻烦也是做这类工具最基本的自我修养。写到这里其实 t3code 本身已经讲得差不多了。最后再分享一点个人体会这套东西真正带给我的不是“省了多少时间”这种可量化的收益而是面对新项目、新环境、新任务时那种“心里有底”的感觉。我知道模板在哪、片段在哪、脚本在哪也知道只要 clone 下来一切都能回来。这种感觉比任何单一工具都让我安心。