
前端 Monorepo 里改一行代码就触发全量构建,既浪费资源又拖慢反馈。本文介绍如何用腾讯云 CNB 的声明式流水线,通过分支匹配、事件触发与 Monorepo 按需构建,让前端 CI/CD 只跑真正需要跑的部分。
做过前端的同学大多遇到过这种场景:团队把多个业务线、多个公共组件塞进同一个 Monorepo 仓库,结果任何一个人随手改了一行样式或一个工具函数,CI 就会把整个仓库的所有子项目从头构建一遍。一次构建动辄十几分钟,队列里排着几十个其实毫无关系的任务,真正改动的那个模块反而要等前面的"陪跑"任务跑完才能拿到反馈。
这种全量构建带来的问题很实在。一是资源浪费,明明只动了某个子目录,却把 CPU 和构建额度花在无关项目上。二是反馈变慢,开发者提交代码后要等很久才知道结果,提交到上线的周期被拉长。三是成本失控,尤其是对用量敏感的团队,大量无效构建会快速吃掉免费额度,让月底的账单居高不下。
问题的根源不在于"要不要自动化构建",而在于"能不能让构建足够聪明,只跑该跑的部分"。这正是按需构建要解决的事情。
按需构建的目标可以概括成一句话:让流水线根据"改了什么"来决定"构建什么"。落到前端 Monorepo 场景,通常有三条判断路径。这三条路径从粗到细层层递进,实际配置时往往组合使用,下面逐一拆解。
第一条是按目录判断。Monorepo 里每个子项目都有明确的目录边界,比如 packages/ui、packages/utils、apps/web。当一次提交只改动 packages/utils 下的文件时,流水线应该只构建依赖 utils 的子项目,而不是把 ui 和 web 也拉进来。
第二条是按分支判断。不同分支往往对应不同的发布节奏,比如 main 分支需要完整构建和测试,而 feature/* 特性分支只需要跑增量校验。通过分支匹配,可以为不同分支配置轻重不同的构建流程。
第三条是按事件判断。同样是代码提交,push 到主干、发起 pull_request、手动触发、定时任务,每种事件需要的构建强度并不一样。按需构建允许把这些事件区分开来,分别绑定不同的流水线。
腾讯云 CNB 的声明式流水线恰好把这三条判断路径都内置到了 .cnb.yml 配置里,接下来结合具体配置来看每条路径怎么用。
CNB 支持通过 glob 模式匹配分支名称,为不同分支配置不同的构建流程。前端团队可以据此把主干分支和特性分支的构建策略分开。
下面是一个典型的配置结构,展示如何按分支划分构建范围:
# 主干分支:完整构建
main:
push:
- stages:
- name: install
script: npm ci
- name: build
script: npm run build
- name: test
script: npm run test
# 特性分支:只跑增量校验
"feature/*":
push:
- stages:
- name: install
script: npm ci
- name: build
script: npm run build这里 main 和 feature/* 是两套独立的触发规则。glob 模式 feature/* 会匹配所有以 feature/ 开头的分支,让这些分支走更轻量的流程。分支匹配让团队不必在一条流水线里堆满条件判断,而是用配置结构本身来表达分支策略。
分支匹配解决的是"哪条分支"的问题,事件触发解决的是"什么动作"的问题。CNB 支持多种触发事件,前端团队可以把它们对应到不同的构建强度上。
push 事件:代码推送到分支时触发,适合跑完整的安装、构建和测试。pull_request 事件:发起或更新合并请求时触发,适合跑增量检查和自动化审查,作为合入主干前的质量门禁。web_trigger 事件:在页面上手动触发,适合发布构建这类需要人工确认、但不必频繁自动执行的场景。schedule 事件:定时任务,适合每天固定时间跑一次全量校验或夜间构建。把这些事件和分支匹配组合起来,就能搭出一套"主干 push 全量跑、PR 增量跑、发布手动跑"的分层策略,让构建资源用在刀刃上。
分支匹配和事件触发解决了"什么时候跑、哪条分支跑"的问题,但要真正做到"只跑改动的子项目",还需要 Monorepo 按需构建能力。
CNB 的 Monorepo 按需构建,需要在 .cnb.yml 中为每个子项目显式声明 ifModify 路径匹配规则:平台根据本次提交改动的文件是否命中某个子项目的 ifModify glob,来决定是否触发该子项目的构建和测试。对于前端团队来说,这意味着:
packages/utils,只构建依赖 utils 的下游项目;以两个前端子项目为例,配置结构如下:
main:
push:
packages/ui:
ifModify:
- packages/ui/**
stages:
- name: install
script: cd packages/ui && npm ci
- name: build
script: cd packages/ui && npm run build
packages/utils:
ifModify:
- packages/utils/**
stages:
- name: install
script: cd packages/utils && npm ci
- name: build
script: cd packages/utils && npm run build只有当提交改动了 packages/ui/** 下的文件时,packages/ui 这条流水线才会被触发,packages/utils 同理。这样就把构建范围精确锁定到真正受影响的子项目上。子项目较多时,可以把通用的 stages 通过 include 抽成模板,每个子项目只声明自己的 ifModify 和差异参数,避免重复粘贴。
这种按影响范围构建的方式,把"改一行代码触发全量构建"带来的浪费有效收敛,让每次构建的耗时和成本都贴近真实的改动规模。
需要注意的是,按需构建的判断依赖 ifModify 路径规则与提交实际改动文件的匹配。如果一次提交同时改动了多个子项目共享的公共依赖,或者改动了被广泛引用的基础包,那么命中规则的下游项目会变多,构建范围也会相应扩大。因此团队在拆分 Monorepo 目录结构时,合理划定子项目边界、尽量减少跨项目的隐式依赖,能让按需构建的判断更精准,避免因为目录划分不清而导致"该跳过的没跳过"。
按需构建让流水线跑得更少,缓存机制则让重复跑的部分变得更快。CNB 提供构建缓存能力,可以把上一次构建产生的中间产物(如依赖包、编译缓存)保留下来,供下一次构建复用。
对前端项目而言,缓存收益最明显的是依赖安装环节。npm ci 这类命令在依赖版本没有变化时,完全可以复用上一次拉取的依赖缓存,省掉重复下载和解析的时间。把缓存和按需构建配合起来,就能同时拿到"跑得更少"和"跑得更快"两重收益。对日均提交频繁的团队来说,这两项叠加后,月度构建额度和耗时的下降通常比较明显。
把分支匹配、事件触发、Monorepo 按需构建和缓存组合在一起,可以得到一份贴近实际的前端按需构建配置。下面以 Monorepo 中两个子项目为例,主干分支按改动路径触发、特性分支走轻量流程:
main:
push:
packages/ui:
ifModify:
- packages/ui/**
stages:
- name: install
script: cd packages/ui && npm ci
- name: build
script: cd packages/ui && npm run build
- name: test
script: cd packages/ui && npm run test
packages/utils:
ifModify:
- packages/utils/**
stages:
- name: install
script: cd packages/utils && npm ci
- name: build
script: cd packages/utils && npm run build
"feature/*":
push:
- stages:
- name: install
script: npm ci
- name: build
script: npm run build这份配置里,main 分支下每个子项目通过 ifModify 锁定各自的改动目录,只有命中的子项目才会安装、构建和测试;特性分支走更轻量的安装与构建流程。再叠加缓存机制复用依赖、发布类构建用 web_trigger 手动触发,就能把前端 CI/CD 从"无差别全量跑"改造成"按需精准跑"。
改一行代码不该等于一次全量构建。前端团队完全可以通过分支匹配划定范围、用事件触发区分强度、用 Monorepo 按需构建锁定受影响的子项目,再配合缓存压缩重复耗时,让 CI/CD 真正"按需跑"。腾讯云 CNB 把这些能力都收敛进了声明式的 .cnb.yml 配置里,不用维护额外的调度脚本,一份配置就能把构建策略表达清楚。
落地时建议从改动最频繁、构建最耗时的那个 Monorepo 仓库开始试点,先把分支匹配和按需构建跑通,观察到资源节省的实际效果后,再逐步推广到全部项目,并顺手把缓存策略和事件分层固化进配置。想动手尝试的话,可以先在 腾讯云 CNB 上用一个真实的前端 Monorepo 仓库跑通第一条按需流水线。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。