给 AI Agent 搭围栏:Monorepo 是被低估的 Agent 基础设施

权限码改了四处、忘了三处:我用一次 Monorepo 改造终结契约漂移,以及为什么 AI Agent 时代它变得更重要

一个所有前后端分离团队都经历过的凌晨

事情从一个"低级 bug"开始。

线上有个按钮,前端藏着,接口却能打------一个越权漏洞。排查两小时,根因令人窒息:

  • 后端 permission-code.ts 里加了权限码 system:user:disable,接口守卫正常生效;
  • prisma/seed.ts 的权限树忘了登记,数据库里没有这个码;
  • 前端按钮上写的是手打的字符串字面量 'system:user:active',和后端差一个单词。

同一个"权限码",活在三个地方,靠一份 Markdown 里的"三步走规则"人工维持同步。规则写得再醒目,也挡不住第 100 次修改时的一次走神。

如果你也干过"改了接口忘了改前端类型"、"A 仓库发版了 B 仓库还在读旧字段"、"两个仓库各有 3000 行几乎一样的 utils"------那么这篇文章是写给你的。

我刚完成了一次彻底的改造:把一个跑了很久的前后端分离工程,重构成了 pnpm workspaces + Turborepo 的 Monorepo。390 个文件变更,git 历史完整保留,全仓 lint/typecheck/test/build 一次性全绿。

这篇会讲清楚四件事:

  1. Monorepo 的好处到底"实"在哪里(不是背概念,是我亲手验证的);
  2. 主流技术选型(pnpm / Turborepo / Nx / Bazel)的架构对比与取舍;
  3. 一份可照抄的实施方法------包括我踩过的 4 个坑和解法;
  4. 重点:在 AI Agent 写代码的 2026 年,Monorepo 为什么从"工程效率选择"变成了"AI 质量的地基"。

一、先分清概念:Monorepo ≠"把所有代码丢一个文件夹"

Monorepo Polyrepo(多仓)
定义 多个项目/包共享一个仓库、一次提交、一套工具链 每个项目独立仓库、独立发版
代表 Google(全公司一个仓)、Meta、Uber、Vercel(Next.js + Turborepo) 绝大多数中小团队现状
依赖同步 编译器 + 同一次提交天然保证 靠发包、锁版本、CHANGELOG 约定
工具链 一套规则治理全部代码 每仓各自演化,逐渐漂移

两个经典误解要先破掉:

  • "Monorepo = 一堆不相关项目的杂物间" ------不是。Monorepo 的核心是相关性:前后端共享接口契约、多个应用共享组件库、一堆微服务共享 proto 定义,这些"强耦合的代码"放一起才叫 Monorepo。
  • "单仓就是 Monorepo" ------也不是。没有 workspace 拆分、没有统一任务编排的单体大仓只是"单体"。Monorepo 的关键在边界清晰 (包与包之间显式依赖)+ 执行统一(一条命令管全仓)。

判断你需不需要它,只需要回答一个问题:你的两个工程之间,有没有一条"改了这边、那边必须跟着改"的线? 有,你就已经在为拆仓付税了。


二、好处:我这次改造实打实拿到的五条

1. 契约同步从"靠自觉"变成"结构保证"

这是本次改造收益最大的一条。

改造前,权限码的真实状态是"1 + 3":后端有一个标榜自己是"唯一事实来源"的常量文件,但前端还有 4 个页面、19 处字符串字面量在手工复读它,seed 里还有一份树状登记。四份状态,全靠文档纪律对齐。

改造后,我建了一个 packages/contracts 包,权限码常量收进去:

typescript 复制代码
// packages/contracts/src/permission-code.ts ------ 唯一事实来源
export const PermissionCode = {
  SystemUserCreate: 'system:user:create',
  SystemUserDisable: 'system:user:disable',
  // ...
} as const

后端守卫装饰器引用它,seed 引用它,前端也引用它:

vue 复制代码
<!-- apps/web:编译器保证这个常量必须存在 -->
<script setup lang="ts">
import { PermissionCode } from '@nestperm/contracts'
</script>

<template>
  <el-button v-auth="PermissionCode.SystemUserDisable">禁用</el-button>
</template>

效果是质变的 :常量改名,两端 20 处引用一起报编译错;常量删除,谁引用谁挂;拼错一个字母,tsc 直接拦下。"改了四处忘了三处"这类 bug 不是被测试抓出来的------是根本编译不过去。

这就是 Monorepo 的第一性原理:让跨边界的约束从"文档约定"降维成"编译器检查"。

2. 原子提交:一个功能 = 一次提交,永远是完整状态

拆仓最痛的不是建仓库,是跨仓变更的中间态。

改一个接口字段,要:A 仓改后端 → 发版 → B 仓升依赖 → 改前端类型 → 发版。中间任何一个环节卡住,main 就是一个"契约对不上"的坏状态,谁拉下来都跑不绿。跨仓 PR 的 review、回滚、bisect 查 bug,全是双倍成本。

Monorepo 里,schema 迁移 + 后端接口 + 前端类型 + 页面 + 权限码,一次 git commit 全部落地 。git bisect 永远在一致的状态集合里切;回滚永远是回滚一整个功能。我这次改造本身也是证明------390 个文件、五个层面的改动,一个 commit 完成,permission-code.ts 从后端目录迁到 contracts 包,git 还自动识别为 rename(86% 相似度),blame 历史一行不丢。

3. 工具链从两套收敛成一套,漂移被"逼"着解决

改造前这个仓库的真实状态(不是段子,是本次迁移的现场):

  • 2 个 pnpm-lock.yaml;
  • 2 套 husky(其中一套是死的,因为 web 根本不是独立 git 仓库);
  • 2 套 commitlint,type-enum 规则不一致 ------后端不许 build,前端偏要 build;
  • 2 套 prettier:一个 semi: true,一个 semi: false。

每个"不一致"都是某次提交被钩子拦住、查半天发现是配置打架的素材。

合并 Monorepo 不是简单删文件------它逼你在一次提交里把这些历史分歧正式裁决掉 :type-enum 合并(build/wip 收编)、prettier 按目录就近生效、lint-staged 拆成根/server/web 三份分别挂在同一个 pre-commit 里。

从此工具链只有一种演化方向,不会再各自漂移。依赖版本也获得了统一控制点------这一点后面踩坑环节会看到它救了我一次。

4. 一条命令的全局质量门禁

bash 复制代码
pnpm lint && pnpm typecheck && pnpm test && pnpm build

Turbo 编排,contracts 先构建,两端各自检查,改了前端只重跑前端(内容寻址缓存)。配合 git hooks,pre-push 挡住的是"整个系统"而不是"某个仓库"。

对小团队尤其关键:你没有专职 CI 工程师,质量门禁的成本必须趋近于零才可能被坚持执行。两套仓库要维护两条流水线、两个徽章、两套缓存;Monorepo 是一条。

5. 扩展成本:加一个应用 = 加一个目录

C 端 App、文档站、Mock 服务、定时任务......在 Monorepo 里都是 apps/ 下建目录 + "@nestperm/xxx": "workspace:*" 一行依赖的事。工具链、契约、hooks、CI 全部白嫖。

拆仓时代每加一个仓库,意味着:重新 init、重新配 lint/prettier/commitlint/husky、重新搞 CI、重新想跨仓依赖怎么发包。第 N 个仓库的边际成本,是 Monorepo 的十倍。


三、架构对比与选型:别无脑上 Bazel

Monorepo 的任务编排工具是个光谱,从"零框架"到"工业化":

方案 本质 优点 缺点 适合谁
npm/yarn workspaces 包管理器内置的 workspace 零新增依赖,node_modules 链接是原生行为 没有任务编排和缓存 ,跨包任务得自己写 --filter 脚本 包很少、任务简单的仓库
pnpm workspaces + 手写脚本 workspaces + 根脚本 pnpm -r 极简、心智低、磁盘效率高(内容寻址存储) 复杂依赖图的执行顺序要手工维护 2~5 个包的小中型仓库
pnpm workspaces + Turborepo 上面 + 任务 DAG + 本地/远程缓存 配置就一个 turbo.json;增量极快;学习成本半天 只做任务编排,不做代码生成/依赖图谱治理 绝大多数前后端 + 组件库的团队(我的选择)
Nx 全家桶:任务图 + 代码生成器 + 插件生态 + affected 测试 依赖图分析最强;nx generate 脚手架统一;大团队治理能力 侵入性强,概念多(workspace.json、executor、generator),迁移和升级都有心智税 百人以上、多技术栈、要长期治理的大单体
Rush(Microsoft)/ Pants 企业级,强调严格性与隔离 依赖审计、版本策略很严 配置繁琐,生态小 超大仓库、强合规场景
Bazel(Google) 构建系统级,hermetic 构建、远程缓存 极致的可重现性与大规模性能 每个语言/工具都要写 BUILD 规则,接入一个新工具是天级成本 Google 级规模(百万级目标),普通团队是自虐

我的选型逻辑,供直接抄:

  1. 先问要不要任务编排------包只有 2~3 个、CI 三分钟跑完?纯 pnpm workspaces 就够,别加东西(YAGNI)。
  2. 要编排和缓存 → Turborepo。理由很朴素:它只做一件事(DAG + 缓存),配置 30 行,不入侵你的构建工具;你的 lint/typecheck/build 每次全跑要几分钟,缓存命中后秒级。
  3. Nx 的问题不是不好,是贵------它的强项(代码生成、大规模治理)在 2~5 包的团队里用不上,弱项(心智负担)却全款支付。
  4. Bazel 和"我需要 Monorepo"之间隔了两个数量级的规模,别被 Google 的故事唬住。

另一个容易忽略的选型维度:包管理器的磁盘策略 。pnpm 的内容寻址存储 + node-linker=hoisted 组合,在 Windows 和 Docker 多阶段构建下有奇效(符号链接在 COPY 时会断,hoisted 平铺结构直接可拷贝)------这是我在 Docker 环节真实踩到的点。


四、实战:把一个分离工程改造成 Monorepo(可照抄的 7 步)

讲方法论不如讲我刚做完的这次。目标仓库:NestJS 后端(根目录)+ Vue3 管理端(web/ 子目录,独立 lockfile)。

Step 1:先画包边界,再动一个文件

动手前用 10 分钟回答:谁和谁共享什么?

vbscript 复制代码
apps/
  server/     ← NestJS(原来在根目录)
  web/        ← Vue3(原来在 web/)
packages/
  contracts/  ← 新建:前后端共享的契约(权限码、响应信封、分页类型)

判断准则:跨边界的重复就是包的种子 。我扫了一遍,前后端真正共享的只有契约类(权限码 + 响应形状),组件库、工具函数两端用不到一起------所以只建一个 contracts,没有为了"架构好看"造 utils、types、shared 三个空包(YAGNI)。

Step 2:git mv 迁移,历史一个都不能丢

bash 复制代码
mkdir -p apps/server apps/web
git mv src prisma test http nest-cli.json tsconfig*.json eslint.config.mjs apps/server/
git mv web apps/web

必须用 git mv 而不是文件管理器拖 ------rename 检测靠的是内容相似度 + git 的索引记录,git mv 后 git 仍能识别 91% 相似文件为同一文件的移动,git log --follow 畅通无阻。

💀 踩坑 1:git mv web apps/web 时如果 apps/web 已被 mkdir 提前建好,git 会嵌套成 apps/web/web。先 mv 后 mkdir,或 mv 后立刻 git status 核对 ,我这次就中招了,靠 git add -A 修正索引。

Step 3:搭 workspace 骨架(10 分钟的活)

yaml 复制代码
# pnpm-workspace.yaml
packages:
  - apps/*
  - packages/*
jsonc 复制代码
// turbo.json ------ 全部配置就这么多
{
  "tasks": {
    "build":      { "dependsOn": ["^build"], "outputs": ["dist/**"] },
    "typecheck":  { "dependsOn": ["^build"] },
    "lint":       {},
    "test":       { "dependsOn": ["^build"] },
    "dev":        { "cache": false, "persistent": true }
  }
}

关键设计:^build(先构建我的依赖)是契约包的命脉 。contracts 不先编译出 .d.ts,两端的 typecheck 全是错。把依赖声明写进 DAG,而不是写进 README。

根 package.json 退化为纯编排器:依赖只剩 turbo/husky/commitlint/prettier,脚本全部 --filter 指向具体包。各包自己的依赖搬回各包------根目录不再有业务依赖,这是"仓库根"和"包"的边界线。

Step 4:契约包双端接线(本方案的灵魂)

contracts 用 tsc 编成 CJS + .d.ts(后端 NestJS require 直接吃)。两端接线各有一处讲究:

后端------保留旧文件做 re-export,全仓 import 路径零改动:

typescript 复制代码
// apps/server/src/shared/constants/permission-code.ts
export { PermissionCode, type PermissionCodeValue } from '@nestperm/contracts'

前端------19 处字符串字面量一次性替换成常量引用(脚本 + 断言,不是手改):

bash 复制代码
# python 脚本批量替换,每处 assert 锚点存在,替换后再扫一遍确认无漏网字面量

💀 踩坑 2(本次最隐蔽的坑):CJS 的 export * 产出的是 getter 间接导出 (Object.defineProperty(exports, ...)),Rollup 静态分析不了,vite build 报 PermissionCode is not exported。typecheck 全过、打包才炸。 解法:web 的 vite.config alias 和 tsconfig paths 直连 TS 源码 (../../packages/contracts/src/index.ts),vite 吃源码天经地义;后端继续走 dist。双端消费同一个包,路径却可以各取最优------记住这个模式,它是 CJS/ESM 混用的通用解。

Step 5:hooks 与提交规范的"合并裁决"

bash 复制代码
# .husky/pre-commit ------ 三份 lint-staged 各管各的辖区
pnpm lint-staged                        # 根目录文件
pnpm --filter @nestperm/server exec lint-staged
pnpm --filter @nestperm/web exec lint-staged

lint-staged 放各包内、钩子在根统一触发,是 monorepo 的标准姿势:规则跟着包走(包内才知道自己用什么 lint),执行跟着仓库走 。两套 commitlint 合并时被迫做了一次规则裁决(后端的 conventional vs 前端的 cz-git 交互配置------全收编进根配置,prompt 字段 commitlint 不看、cz-git 正好看)。

Step 6:Docker 适配------workspace 感知的构建

拆仓时代的 Dockerfile 复制的是根目录 package.json,现在必须感知 workspace:

dockerfile 复制代码
# 只复制需要的 manifest,让层缓存生效
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml .npmrc ./
COPY apps/server/package.json ./apps/server/
COPY packages/contracts/package.json ./packages/contracts/
RUN pnpm install --frozen-lockfile --filter @nestperm/server...
# 构建顺序与 turbo 一致
RUN pnpm --filter @nestperm/contracts build && pnpm --filter @nestperm/server build

注意 .dockerignore 要补 apps/web------前端源码根本不该进后端镜像的构建上下文(拆仓时代它是另一个仓库,天然进不来;合仓了不加这条,构建上下文体积翻倍)。

Step 7:全量验证 + 文档同步

bash 复制代码
pnpm lint && pnpm typecheck && pnpm test && pnpm build && pnpm check:circular

一次跑全绿才算完。最后同步 AGENTS.md/README 里所有路径与命令------文档里的死路径是 Monorepo 改造后最常见的"技术债化石"。

我踩的另外两个坑(快讲)

💀 踩坑 3:删旧 lockfile 导致依赖漂移 。两个旧 lockfile 删掉重新解析,vue 3.5.22→3.5.44、element-plus 2.11.4→2.14.7,模板组件类型收紧,一次炸出 41 个类型错 。 解法:根 pnpm.overrides 把核心依赖钉回已验证版本。教训:结构迁移和依赖升级必须是两个 commit------迁移期的唯一目标是"和以前行为一致地换结构"。这也顺手给仓库立了条规矩:想升级依赖?先跑全仓三件套。

💀 踩坑 4:prisma 引擎下载卡死 。原生依赖的 postinstall 走的不是 npm registry(Prisma 引擎直连境外 CDN),npm 换镜像救不了它,进程静默挂住、还锁死 node_modules 目录(EBUSY 连 rm 都删不掉)。解法:HTTPS_PROXY=... pnpm install。教训:Monorepo 让"一个卡住的 postinstall"阻塞整个仓库的安装------换以前只有后端那一仓受影响。这是合并的代价,要认。


五、重点:AI Agent 开发场景下,Monorepo 为什么从"可选"变"必选"

上面四节是 2023 年就能写的道理。这一节是 2026 年的增量------当写代码的主力从"人"变成"人 + Agent",Monorepo 的收益曲线变了。

1. 契约包 = 给 Agent 上的"编译器围栏",直接对抗幻觉

LLM 最大的工程化问题不是"写得慢",是自信地写错。

拆仓 + 字符串字面量的世界里,Agent 补全一个 v-auth="'system:user:disabled'"(注意:是 disabled 不是 disable),没有任何机制会报错------运行时 403,人排查两小时,就是文章开头那个故事。Agent 生成代码的速度越快,这类"看起来对、跑起来错"的幻觉产出就越多。

Monorepo + 契约包把这件事彻底反转:

typescript 复制代码
import { PermissionCode } from '@nestperm/contracts'
v-auth="PermissionCode.SystemUserDisbale"  // 拼错 → tsc 立即报不存在的属性

Agent 不需要"记住"权限码------编译器替它记 。它幻觉出一个不存在的码,typecheck 直接红;它想用被删除的码,编译直接拦。契约在 packages/contracts 里躺着,Agent 每次 grep 都能拿到同一份、最新的、唯一的事实,而拆仓世界里它可能读的是三个月没更新的 B 仓 README。

这是本质区别:拆仓时代你靠 prompt 求 Agent 别搞错,Monorepo 你靠类型系统让它错不了。AI 时代,"错误能否被机器快速发现"比"错误是否会发生"重要得多------因为错误一定会发生。

2. 单一质量门禁 = Agent 的"奖励函数",一条命令闭环

Agent 的工作循环是:改代码 → 自验证 → 根据反馈修 → 再验证。这个循环的质量决定 Agent 产出的质量,其中"自验证"环节最怕两件事:跑得慢、不知道该跑什么。

Monorepo 恰好两个都解了:

bash 复制代码
pnpm lint && pnpm typecheck && pnpm test   # 就这一条,Agent 记一条规则就够
  • 确定性 :AGENTS.md 里写一条"改完必跑这三件套、全绿才算完成",Agent 在任何目录、改任何包,执行的都是同一条命令。拆仓时代要写"后端仓库跑 A、前端仓库跑 B、契约变更两边都跑且注意顺序"------规则越复杂,Agent 越容易漏。
  • 速度 :Turbo 的内容寻址缓存意味着 Agent 改了前端页面,后端测试直接命中缓存不重跑,验证循环从"几分钟"压到"几十秒"。Agent 一天迭代几十轮,每轮省 3 分钟就是巨大的吞吐差异。
  • 原子性 :Agent 改一个功能横跨 schema + 后端 + 前端,Monorepo 里一次提交、一次验证、一次回滚。拆仓时代 Agent 要么跨两个仓库各提一次(中间态是坏的),要么干脆只改一半------跨仓原子操作对单会话 Agent 是超纲题。

3. 单根上下文 = 省下 Agent 最贵的资源:Context Window

Agent 的上下文是按 token 算钱的。Monorepo 给它三个直接节省:

  • 一次搜索,全貌到手 :grep PermissionCode 一条命令扫到两端所有消费点;拆仓要开两个目录、拼两次结果,还可能其中一边没 clone。
  • 单根 AGENTS.md :行为规范、分层红线、命令速查写在仓库根,所有包共享同一套指令。拆仓要维护两份规范文档,必然漂移------而漂移的规范对 Agent 是毒药(它会严格执行错的那份)。
  • 物理连续性 :Agent 的文件操作、编辑历史、工作区状态都在一棵树里,不需要跨仓库切换会话状态。这不只是方便------跨仓切换是 Agent 丢上下文、开始"张冠李戴"的高发场景(把 A 仓的路径习惯带进 B 仓,把 B 仓的规范用在 A 仓)。

4. AI 让 Monorepo 改造本身的成本,第一次降到了"半天"

过去不选 Monorepo 的头号理由从来不是"不好",是**"迁移太贵"**:梳理包边界、搬文件、修路径、调 CI、适配 Docker、同步文档------按传统经验这是一次"一周起步、中途大概率回滚"的重构,ROI 算不过来。

现在这笔账变了。这次改造由 AI Agent 主导完成:390 个文件的迁移与接线、批量替换带断言的脚本、Docker/hooks/文档全量适配、四个坑的定位与修复、全量验证,一个工作会话内完成,全程 git 历史零丢失,一次推上远端。

(Agent 也踩了坑------比如它自己 cd 进了待删除目录导致 EBUSY 锁死,排了半小时才发现是自己的 shell 占着目录。人机协作的黑色幽默,但成本是半小时,不是半周。)

这是一个临界点 :当"迁移成本"从周级降到小时级,而"契约收益"随着 Agent 使用量线性上升,ROI 曲线交叉了。你每多让 Agent 写一行跨端代码,拆仓的税就多收你一分。

5. 反过来:Monorepo 也在"驯化" Agent 的行为

有个容易被忽略的因果:Monorepo 的结构本身就在减少 Agent 的自由发挥空间。

  • 包边界明确了"什么该放哪"------Agent 不会再把前端工具函数混进后端 shared(apps/ 和 packages/ 的命名就是给它看的目录语义);
  • 单向依赖规则(apps → packages,禁止反向)写在 turbo.json 的 DAG 里,violation 直接构建失败------规则不再依赖 Agent "记得遵守";
  • 统一的响应包、DTO 白名单、错误码集中登记,这些模式在单仓里高度重复可见,Agent 照猫画虎的准确率远高于在五个仓库里各学一遍。

人靠 Code Review 约束 AI,那是事后;靠结构约束 AI,那是事前。 Monorepo 就是那个结构。


六、诚实地聊聊代价(不谈缺点的安利文都是耍流氓)

  1. 依赖耦合:全仓一个 lockfile,一个依赖解析变动可能波及全仓------这次 41 个类型错就是学费。对策:overrides 钉版 + 结构变更与依赖升级严格分离。
  2. 安装与构建体积 :pnpm install 一次装全仓,比单仓重;Docker 要写 --filter。对策:pnpm 内容寻址去重 + turbo 缓存 + manifest 先行的层缓存。
  3. 权限与噪音 :仓库越大,无关变更越容易进 diff,git status 噪音越多。对策:CONTRIBUTING 约定 + 目录级 OWNERS(大团队才需要)。
  4. 一个卡点阻塞全仓:如踩坑 4,原生依赖 postinstall 挂了,所有人的 install 都过不去。这本质是"集中"的代价,认。
  5. 它不适合谁 :多团队完全独立发版、毫无共享代码的组织;对保密有严格边界要求的场景。没有共享契约的多仓不是 Monorepo 的反面,是与你无关的场景。

结语:把"别忘了同步"从纪律变成物理定律

回到开头那个凌晨。

改造之后,同样的一次改动变成了这样:在 contracts 里加一个常量,后端装饰器引用它,seed 引用它,前端引用它------然后跑 pnpm typecheck。三个消费点里任何一个没跟上,命令就是红的。 没有"记得登记"、没有"别忘了改前端"、没有 code review 时逐个仓库对字符串。

这就是 Monorepo 在 AI 时代的完整叙事:

  • 对人,它是契约同步、原子提交、工具链统一的工程效率选择;
  • 对 AI ,它是编译器围栏(对抗幻觉)、单一验证闭环(Agent 的奖励函数)、单根上下文(省 token)、结构化约束(驯化自由发挥)的质量地基;
  • 对现在,AI Agent 把迁移成本从"周"打到了"小时",让这道 ROI 计算题第一次对中小团队完全倒向"该做"。

当你的代码里有 30% 出自 Agent 之手时,你最庆幸的不会是"我们用了哪个框架",而是------所有需要保持一致的东西,编译器替你盯着,而且 Agent 错不了。


如果这篇对你有启发,点赞收藏一条龙是我继续拆解真实改造细节的最大动力。文中方案基于 NestJS 11 + Vue3 + pnpm 10 + Turborepo 2 的实战,turbo.json / pnpm-workspace.yaml / hooks 配置均可直接抄。评论区聊聊你团队的仓库形态,我在线答疑。

相关推荐
橙子家1 小时前
Exception 简介以及都有哪些类型【C# 基础】
后端
数据掘金1 小时前
白屏故障定位:从资源请求到路由渲染的排查顺序
前端
程序员Sunday1 小时前
ES Module 和 CommonJS 有什么区别?循环依赖时会发生什么?
前端·javascript·面试
flash俊杰1 小时前
agent 节点、人工授权竞速邮箱与四态 golden 评测
后端
玲珑意1 小时前
Vue 3 + DAG拓扑排序 + SSE流式人机协同:现代AI工作流编排系统的工程化实践
前端
三十而立洋1 小时前
TypeScript 7.0 深度解析:新特性、Go 原生编译器及与 TypeScript 6.0 的区别
前端·typescript·前端工程化
编程之道1 小时前
从VUE2到VUE3学习过程遇到的一些困惑
前端
用户817595220131 小时前
QNAP NAS 搭建个人网站实战
前端
高晶2 小时前
Android 电子词典开发实战指南
后端·程序员