前端工程化到底在解决什么:从一个项目越改越慢说起

前端工程化到底在解决什么:从一个项目越改越慢说起

本文是「前端工程现场」系列的第 1 篇。

这一系列不打算只介绍工具怎么配置,而是从真实开发问题出发,聊聊前端项目为什么会逐渐变难改,以及工程化到底能帮我们解决什么。

一个前端项目刚开始时,通常都很美好。

页面不多,组件不多,src 目录也很干净。产品提一个需求,找到对应页面,改完代码,本地跑一下,顺手提交。

整个过程流畅得让人产生一种错觉:

这个项目以后应该也挺好维护的。

再过几个月,项目还是那个项目,开发体验却逐渐变了。

加一个筛选条件,要先确认状态到底藏在哪;改一个公共组件,发现它被七八个页面悄悄依赖;本地明明运行正常,CI 却像第一次见到这份代码;线上出现问题后,大家围着发布记录研究半天,试图找出这次构建到底混进了什么。

代码没有突然变笨,框架也没有趁半夜偷偷退化。

只是当页面、人员、依赖和业务规则一起增加后,原来依靠记忆和默契维持的开发方式,开始不够用了。

这正是前端工程化开始发挥作用的地方。


一、项目变慢,不一定是页面加载变慢

提到"前端项目变慢",很多人第一反应是性能问题:

  • 首屏加载慢;
  • 接口返回慢;
  • 页面渲染卡顿;
  • 打包体积太大。

这些当然属于前端工程的一部分,但本文所说的"慢",更多是另一种慢:

一个需求从提出到稳定上线,所需要的时间越来越长了。

代码可能只改了二十行,开发者却花了半天确认影响范围。

一个按钮可能只换了文案,却要检查它是不是来自远程配置、国际化文件或公共组件。

一个依赖可能只升级了小版本,测试环境却突然白屏。

真正拖慢项目的,往往不是"写代码"本身,而是围绕这次改动产生的大量附带工作:

text 复制代码
找代码入口
→ 理解已有逻辑
→ 判断影响范围
→ 修改代码
→ 本地验证
→ 处理环境差异
→ 等待构建
→ 回归测试
→ 发布上线
→ 出问题后回溯

前端工程化要做的,不是让业务复杂度凭空消失,而是尽量减少这些过程中的混乱和重复劳动。

换句话说:

工程化主要解决的不是代码能不能运行,而是代码能不能被持续、稳定、低风险地修改。

二、从一个"只加筛选条件"的需求说起

假设我们正在维护一个订单管理页面。

产品提出了一个听起来非常正常的需求:

在订单列表中增加"订单来源"筛选条件,并且刷新页面后保留当前筛选状态。

从页面效果看,无非是多一个下拉框。

如果项目规模很小,代码可能是这样的:

vue 复制代码
<script setup lang="ts">
import { ref } from 'vue'

const source = ref('all')

function fetchOrders() {
  // 请求订单列表
}
</script>

给页面加一个变量,发请求时带上参数,需求基本就完成了。

但在一个已经运行了一段时间的项目里,开发者很快会遇到一连串问题:

  1. 筛选条件应该放在页面局部状态、Pinia,还是 URL 查询参数中?
  2. 切换筛选条件后,当前页码要不要重置为第一页?
  3. 页面刷新后保留状态,是读取 URL、sessionStorage,还是全局缓存?
  4. 请求参数是页面自己拼,还是交给统一的查询转换函数?
  5. 返回上一页时,要恢复上次查询,还是重新请求?
  6. 这个页面是否复用了公共表格组件?
  7. 公共表格组件内部是不是已经偷偷管理了分页?
  8. 权限变化后,某些筛选项是否应该隐藏?

需求还是那个需求,下拉框也还是那个下拉框。

只是它已经不是一个孤立控件,而是进入了页面状态、路由状态、请求参数、缓存策略、权限逻辑和公共组件之间的关系网。

这就是项目越改越慢的第一个原因:

功能增加后,代码之间的关系越来越多,但这些关系没有被清楚地表达出来。

开发者真正花时间的地方,不是写下拉框,而是猜测:

我改这里,会不会把别的地方也弄坏?

工程化的第一个目标,就是让这种猜测尽可能少一点。


三、工程化真正关注的是"变更成本"

很多人刚接触前端工程化时,会把它理解为一组工具:

  • Vite 或 Webpack;
  • npm、pnpm 或 Yarn;
  • ESLint、Prettier;
  • TypeScript;
  • Monorepo;
  • Git Hooks;
  • CI/CD。

这些工具都很重要,但它们并不等于工程化。

一个项目装了 ESLint,不代表代码边界就清楚。

一个项目用了 pnpm,不代表依赖一定合理。

一个项目搭了 Monorepo,也不代表它从此进入现代化管理阶段。有时只是把原本一个仓库里的混乱,整齐地分散到了五个包里。

判断一个工程化方案有没有价值,可以看它是否降低了下面几类成本。

成本 常见表现
理解成本 新成员找不到入口,旧成员也要重新回忆
修改成本 一个小需求需要同时改很多不相关模块
验证成本 不知道该测哪些页面,只能全量回归
协作成本 每个人的目录、命名和提交方式都不一样
交付成本 本地正常,测试或生产环境异常
回溯成本 上线出错后,找不到对应代码和构建记录

所以,我更愿意用一句话概括前端工程化:

通过约定、工具和自动化,让项目中的每次变更更加可预测。

所谓"可预测",不是保证永远不出 Bug。

而是尽量做到:

  • 知道代码应该改在哪里;
  • 知道这次改动会影响什么;
  • 知道提交前要经过哪些检查;
  • 知道构建使用了什么环境;
  • 知道发布的是哪个版本;
  • 知道出问题后从哪里开始排查。

如果一套复杂工具链没有让这些事情更清楚,它可能很先进,但不一定真的适合当前项目。


四、第一件事:让代码边界能被看见

项目变乱,往往不是因为开发者完全不会写代码。

更多时候,是因为一段逻辑刚出现时很小,大家觉得"先放这里也没事",后来它不断长大,却再也没有被重新整理。

1. utils 是怎么逐渐长胖的

utils 大概是前端项目里最容易长胖的目录。

刚创建时,它通常只有这些内容:

text 复制代码
utils/
  formatDate.ts
  formatMoney.ts
  debounce.ts

看起来很正常。

几个月后再打开,里面可能已经住进了:

text 复制代码
utils/
  formatDate.ts
  formatMoney.ts
  request.ts
  permission.ts
  user.ts
  track.ts
  orderStatus.ts
  storage.ts
  router.ts
  download.ts
  fixSomePageBug.ts

名字还是 utils,职责已经接近社区服务中心。

问题不在于文件多,而在于开发者无法从目录名称判断:

  • 这些函数是否依赖浏览器环境;
  • 是否会读取全局状态;
  • 是否会发送请求;
  • 是否只适用于订单业务;
  • 是否可以在其他项目中复用。

当一个目录无法表达边界时,所有代码都可能往里面放,所有模块也可能依赖它。

久而久之,utils 就会变成项目里的交通枢纽。大家都从这里经过,但谁也不敢轻易施工。

2. 按职责拆分,而不是按文件类型堆放

对于中小型项目,可以先按业务和职责进行划分:

text 复制代码
src/
  modules/
    order/
      api/
      components/
      hooks/
      pages/
      types/
      utils/
    user/
      api/
      components/
      pages/
  shared/
    components/
    hooks/
    utils/
  infrastructure/
    request/
    router/
    storage/

这里的关键不是目录名称必须一模一样,而是每一层都尽量回答一个问题。

modules/order 表示订单业务内部能力。

shared 表示确实可以被多个业务复用的能力。

infrastructure 表示请求、路由、存储这类基础设施。

这样,当订单页面需要一个特殊的状态转换函数时,我们不会因为它"长得像工具函数",就立刻把它扔进全局 utils

它可以先留在订单模块中:

text 复制代码
modules/order/utils/transformOrderStatus.ts

等到其他业务真的出现相同需求,再考虑抽象。

工程化中的复用,不是看到两行代码相似就马上合并。

更稳妥的顺序是:

text 复制代码
先保证职责清晰
→ 再观察是否重复
→ 最后决定是否抽象

过早抽象的结果,经常是得到一个参数很多、分支很多、名字很通用,但没人敢改的公共函数。

3. 公共组件不应该等于"所有页面都能往里塞逻辑"

公共组件也容易出现类似问题。

例如一个表格组件,最初只负责:

  • 接收列配置;
  • 接收数据;
  • 展示分页。

后来为了减少页面代码,它又开始负责:

  • 自动发请求;
  • 保存查询条件;
  • 读取路由参数;
  • 判断按钮权限;
  • 导出 Excel;
  • 处理不同业务的状态颜色;
  • 打开详情弹窗。

最后它确实"很强大",但每个页面使用时都要传十几个参数。

这种组件看起来提高了复用率,实际上只是把多个业务页面的复杂度集中到了一个文件里。

一个更清晰的拆法是:

text 复制代码
基础表格组件:负责展示和基础交互
业务列表 Hook:负责请求、分页和查询状态
业务页面:负责组合组件与业务规则

例如:

ts 复制代码
const {
  data,
  loading,
  pagination,
  query,
  search,
  reset
} = useOrderTable()

表格组件不需要知道订单业务,订单页面也不用重复编写分页请求逻辑。

这类边界一旦清楚,后续改动就更容易判断应该落在哪一层。

五、第二件事:让依赖关系可追踪

前端项目中至少存在两类依赖。

第一类是 npm 包依赖:

text 复制代码
项目
→ Vue
→ Vue Router
→ Pinia
→ Axios
→ 各种构建插件

第二类是项目内部的代码依赖:

text 复制代码
页面
→ 业务组件
→ 业务 Hook
→ 请求模块
→ 基础设施

包管理工具主要管理第一类。

目录结构、导入规范和模块设计主要约束第二类。

两类依赖任何一边失控,项目都会开始出现一些熟悉的现象:

  • 我的电脑能运行,你的电脑不能运行;
  • 删除一个文件前,全组搜索三遍仍然不放心;
  • 页面通过五层 ../../ 引用了另一个业务模块;
  • 一个公共文件被几十个页面依赖;
  • 依赖升级后,完全无关的页面突然报错;
  • package.json 里没有声明的包,代码却照样能引用。

1. 锁文件不是装饰品

不少项目会把 package-lock.jsonpnpm-lock.yamlyarn.lock 当成安装依赖后顺手生成的文件。

实际上,锁文件承担着一个很重要的任务:

尽量让不同开发者、不同 CI 环境安装出一致的依赖树。

package.json 中经常会出现版本范围:

json 复制代码
{
  "dependencies": {
    "some-package": "^1.3.0"
  }
}

^1.3.0 并不只代表 1.3.0,它允许包管理工具在规则范围内选择更新版本。

如果没有稳定锁文件,不同时间安装依赖,得到的间接依赖可能并不完全相同。

于是就会出现经典场景:

昨天还能跑,今天重新安装依赖后就不行了。

代码一行没改,项目自己获得了新的想法。

所以在团队项目里,锁文件应该提交到版本库,并在 CI 中使用能够严格遵守锁文件的安装命令。

例如 pnpm:

bash 复制代码
pnpm install --frozen-lockfile

如果锁文件与 package.json 不一致,CI 应该直接失败,而不是现场帮你重新生成一份新的依赖答案。

2. 固定的不只是依赖版本

要稳定复现一个前端项目,通常还要明确:

  • Node.js 版本;
  • 包管理器及版本;
  • 环境变量;
  • 构建命令;
  • 操作系统差异;
  • 私有源或镜像配置。

可以在 package.json 中声明:

json 复制代码
{
  "engines": {
    "node": ">=20 <21"
  },
  "packageManager": "pnpm@10.0.0"
}

也可以通过 .nvmrc、Volta 或其他版本管理工具统一 Node.js 环境。

这些配置看起来都不复杂,却能减少很多"我这里没问题"的争论。

因为计算机不会被"我这里真的没问题"说服。

它更相信版本号。

3. 内部依赖也需要方向

除了 npm 包,业务模块之间的依赖方向也应该尽量清晰。

例如可以约定:

text 复制代码
页面可以依赖业务组件
业务组件可以依赖业务 Hook
业务 Hook 可以依赖 API 和基础设施
shared 不能反向依赖具体业务模块

错误方向通常长这样:

text 复制代码
shared/components/Table
→ import modules/order/constants

一旦通用层开始反向依赖业务层,它就不再真正通用。

后续其他模块使用这个组件时,也会被迫带上订单业务的概念。

依赖方向越稳定,修改时越容易判断影响范围。

反过来,如果所有模块都能互相引用,目录结构再漂亮也只是文件夹版的蜘蛛网。


六、第三件事:让开发和构建环境可以复现

本地开发顺利,并不意味着项目已经具备稳定交付能力。

很多问题会在代码离开开发者电脑后才出现:

  • CI 使用的 Node.js 版本不同;
  • 测试环境缺少某个环境变量;
  • Linux 对文件名大小写更敏感;
  • 本地使用缓存,CI 从零安装;
  • 构建插件在不同版本下行为不同;
  • 生产环境的接口前缀配置错误。

这类问题有一个共同点:

项目运行依赖了某些条件,但这些条件没有被明确记录。

一个可复现的构建过程,至少应该接近下面这个公式:

text 复制代码
确定的代码
+ 确定的依赖
+ 确定的运行时
+ 确定的环境配置
= 可预期的构建结果

这里的"确定"不是说所有内容永远不变,而是变化必须可追踪。

1. 环境变量要有边界

前端项目通常会有多套环境:

text 复制代码
开发环境
测试环境
预发布环境
生产环境

环境变量至少应该说明:

  • 变量名称;
  • 变量用途;
  • 哪些变量可以暴露给浏览器;
  • 哪些变量由部署平台注入;
  • 缺失变量时应该直接失败还是使用默认值。

例如:

env 复制代码
VITE_API_BASE_URL=https://example.com/api
VITE_APP_ENV=production

同时提供一份不包含敏感值的示例:

env 复制代码
# .env.example
VITE_API_BASE_URL=
VITE_APP_ENV=

需要特别注意的是:

前端环境变量最终可能进入构建产物,它不是存放真正秘密信息的保险箱。

私钥、数据库密码、服务端密钥不应该因为前面加了一个 VITE_,就突然获得在浏览器里公开展示的资格。

2. 把构建流程交给脚本

项目中的关键操作应该尽量通过脚本执行,而不是依赖口头说明。

json 复制代码
{
  "scripts": {
    "dev": "vite",
    "lint": "eslint .",
    "typecheck": "vue-tsc --noEmit",
    "test": "vitest run",
    "build": "vite build"
  }
}

这样本地、CI 和其他开发者都可以执行同一组命令。

一个常见的 CI 流程可以是:

text 复制代码
安装指定 Node.js
→ 安装指定 pnpm
→ 根据锁文件安装依赖
→ Lint
→ TypeScript 检查
→ 单元测试
→ 构建
→ 保存构建产物

流程不一定要一开始就很复杂。

先让它稳定、快速、可理解,再逐步增加检查。

如果一个 CI 流水线有二十多个步骤,但每次失败只留下一句"Process exited with code 1",那它对开发者的帮助可能还不如本地的一条报错信息。

七、第四件事:把检查放到问题发生之前

很多前端问题并不难解决,难的是它们发现得太晚。

例如一个类型错误:

  • 在编辑器里发现,可能十秒钟就能修好;
  • 在提交前发现,可能一分钟修好;
  • 在 CI 中发现,需要重新提交并等待流水线;
  • 在测试阶段发现,需要重新构建和回归;
  • 在线上发现,就会顺便附赠群聊提醒、紧急回滚和一段难忘的夜晚。

工程化中的质量门禁,本质上是在做一件事:

让能够自动发现的问题,尽可能早地被自动发现。

1. 不同问题放在不同阶段检查

可以按成本分配检查位置。

检查内容 适合阶段
格式化 编辑器保存时
基础 ESLint 规则 本地开发、提交前
TypeScript 类型检查 本地、CI
单元测试 本地按需、CI
构建验证 CI
页面核心流程 预发布环境、E2E
性能和错误 线上监控

并不是所有检查都要塞进 Git 提交钩子。

如果开发者每次提交都要等待十几分钟,他首先学会的可能不是提高代码质量,而是如何跳过钩子。

好的检查应该具备三个特点:

  1. 足够稳定,不要今天通过明天随机失败;
  2. 足够快速,不要明显打断开发节奏;
  3. 报错明确,告诉开发者哪里错了以及如何定位。

2. TypeScript 的价值不只是"有类型"

TypeScript 常被当作前端工程化中的标准配置,但真正有价值的不是把所有变量后面都补上类型。

它更重要的作用是让模块之间的契约变得明确。

例如订单接口返回:

ts 复制代码
interface Order {
  id: string
  source: 'web' | 'miniapp' | 'offline'
  status: 'pending' | 'paid' | 'cancelled'
}

页面、表格、状态转换和接口模块都可以围绕同一份数据结构工作。

当后端字段变化或状态新增时,类型检查能够帮助我们找到受影响的位置。

这比上线后看到页面展示一个 undefined,再从浏览器控制台开始考古,要友好得多。

3. 测试应该保护高价值逻辑

不是每一行代码都需要写单元测试。

对于前端项目,优先保护这些内容通常更划算:

  • 金额和日期计算;
  • 权限判断;
  • 查询参数转换;
  • 状态机;
  • 复杂表单校验;
  • 公共 Hook;
  • 高频使用的基础组件。

例如订单查询参数转换:

ts 复制代码
export function transformOrderQuery(query: OrderQuery) {
  return {
    source: query.source === 'all' ? undefined : query.source,
    page: query.page,
    pageSize: query.pageSize
  }
}

这类函数输入输出明确,很适合单元测试。

而一个只负责展示标题的简单组件,未必需要为了追求覆盖率数字写一堆几乎没有价值的测试。

工程化不是让报表更漂亮,而是保护真正容易出错、出错代价较高的部分。


八、第五件事:让发布结果可回溯

代码通过检查并成功构建,不代表工程化流程已经结束。

项目上线后,还需要回答几个问题:

  • 当前线上运行的是哪个版本?
  • 这个版本对应哪个 Git 提交?
  • 构建时使用了哪些环境配置?
  • 谁在什么时间发布了它?
  • 出现错误后能否快速定位到源码?
  • 新版本异常时能否回滚?

如果这些信息都找不到,线上排查就很容易变成集体猜谜,诶我本地没问题啊~

1. 版本、提交和产物要对应

比较基础的做法是,在构建产物中注入版本信息:

ts 复制代码
export const BUILD_INFO = {
  version: import.meta.env.VITE_APP_VERSION,
  commit: import.meta.env.VITE_GIT_COMMIT,
  buildTime: import.meta.env.VITE_BUILD_TIME
}

然后在系统的"关于"页面、控制台或监控上报中带上这些信息。

这样出现问题时,至少可以确认:

用户当前看到的页面,到底是哪一次构建。

不要小看这一步。

很多线上问题最尴尬的阶段,不是不会修,而是大家讨论了十分钟后才发现,有人看的测试环境,有人看的生产环境,还有人浏览器里一直开着上周的缓存页面。

2. 监控不是上线后才临时打开控制台

前端监控至少可以覆盖:

  • JavaScript 运行错误;
  • Promise 未处理异常;
  • 接口失败;
  • 资源加载失败;
  • 白屏或关键页面异常;
  • Core Web Vitals 等性能指标;
  • 用户操作链路;
  • 发布版本信息。

监控的目标不是收集越多数据越好。

更重要的是让错误具备上下文:

text 复制代码
哪个用户
在哪个页面
执行了什么操作
使用哪个版本
发生了什么错误
是否影响核心流程

只有错误堆栈,没有版本和操作信息,排查时仍然可能一头雾水。

3. 回滚能力也是工程能力

发布系统不应该只会前进。

当新版本出现严重问题时,团队需要能够快速恢复到上一份稳定产物。

这里强调的是"上一份稳定产物",而不是临时切回旧代码再现场重新构建。

因为重新构建可能使用不同依赖、不同环境或不同配置,得到的东西未必真的是原来的版本。

可回溯的产物管理,会让回滚更可靠。

九、工程化不是工具越多越好

聊到这里,很容易产生另一种误解:

既然工程化这么重要,那是不是应该把所有工具一次性装齐?

并不是。

一个只有两三页的活动页面,可能不需要 Monorepo、微前端、复杂状态管理和完整组件平台。

一个三人团队维护的单体后台,也未必需要先花两周设计一套"支持未来二十个项目"的基础架构。

工程化方案本身也有成本:

  • 学习成本;
  • 配置成本;
  • 维护成本;
  • 升级成本;
  • 团队沟通成本;
  • 构建时间成本。

判断一个工具是否值得引入,可以先问四个问题:

text 复制代码
1. 当前项目是否真的存在这个问题?
2. 这个问题出现得是否足够频繁?
3. 工具带来的收益是否高于维护成本?
4. 团队成员能否理解并长期使用它?

例如项目只有一个应用,却为了"显得工程化"引入复杂 Monorepo,可能会增加额外配置。

但如果团队已经维护多个共享组件、多个应用和多个独立发布单元,Monorepo 就可能明显降低依赖同步和版本管理成本。

工具没有绝对先进或落后。

只有是否适合当前问题。

工程化不是把项目装修成工具展览馆,而是持续清理开发过程中的摩擦。


十、已经很乱的项目,应该从哪里开始

如果一个项目已经运行了很久,目录、依赖和流程都有些混乱,通常不适合突然宣布:

下周停止业务开发,我们进行一次彻底重构。

这种计划听起来很有决心,最后往往会变成一条长期存在、谁也不敢合并的重构分支。

更稳妥的方式,是从最频繁、最影响交付的问题开始。

场景一:依赖安装经常不一致

先做这些:

text 复制代码
统一包管理器
→ 提交并维护锁文件
→ 固定 Node.js 和包管理器版本
→ CI 使用冻结锁文件安装

场景二:公共组件修改风险很高

先做这些:

text 复制代码
梳理高频公共组件
→ 记录主要使用场景
→ 移除明显的业务逻辑
→ 为核心行为补测试
→ 明确变更规则

场景三:页面状态越来越混乱

先区分:

text 复制代码
组件内部临时状态
页面查询状态
URL 可分享状态
跨页面全局状态
服务端数据状态

不是所有状态都应该放进 Pinia,也不是所有筛选条件都应该藏在组件内部。

场景四:发布经常出问题

先做这些:

text 复制代码
统一构建命令
→ 明确环境变量
→ 在 CI 中执行完整构建
→ 保存构建产物
→ 记录版本与提交关系

工程化改造最有效的方式,通常不是一次把所有问题解决,而是每解决一个高频摩擦,就把它沉淀成团队可以重复执行的规则或自动化流程。


十一、如何判断项目是否需要加强工程化

可以用下面这份检查表做一次简单盘点。

开发环境

  • 新成员能否根据 README 在半天内运行项目?
  • Node.js 和包管理器版本是否明确?
  • 依赖安装结果能否稳定复现?
  • 环境变量是否有示例和说明?

代码组织

  • 新需求到来时,能否快速找到对应模块?
  • 业务代码和通用能力是否有清晰边界?
  • 公共组件是否混入大量具体业务规则?
  • 模块依赖方向是否基本稳定?

质量检查

  • 类型错误能否在合并前发现?
  • 核心业务逻辑是否有必要的测试?
  • CI 失败信息是否足够明确?
  • 检查流程是否稳定且不会过度拖慢开发?

构建发布

  • 本地和 CI 是否执行相同构建命令?
  • 每次发布是否对应明确的提交和产物?
  • 线上错误是否带有版本信息?
  • 出现严重问题时是否能够快速回滚?

这份检查表不是评分表。

没有全部做到,也不代表项目不合格。

更有价值的用法是找出:

哪一项正在让团队反复浪费时间?

工程化应该优先解决这个问题,而不是先挑一个最近很火的工具安装进去。


十二、小结

前端项目越改越慢,通常不只是因为代码数量增加了。

更深层的原因是:

  • 模块边界逐渐模糊;
  • 依赖关系难以追踪;
  • 开发环境无法稳定复现;
  • 问题发现得太晚;
  • 构建发布缺少记录;
  • 线上结果难以回溯。

前端工程化做的,就是通过目录约定、依赖管理、类型系统、自动化检查、构建流程和监控体系,让每一次改动更容易理解、更容易验证,也更容易安全地交付。

它不是某个具体工具,也不是一套固定答案。

Vite、pnpm、TypeScript、ESLint、Monorepo 和 CI 都只是手段。

真正的判断标准始终是:

它有没有降低项目的变更成本?

下一次再遇到"明明只是改了两行代码,为什么花了半天"的情况,可以先别急着怪需求、怪框架或者怪昨天写代码的自己。

顺着这次改动的链路看一遍:

text 复制代码
入口是否难找?
边界是否模糊?
依赖是否混乱?
环境是否不一致?
检查是否太晚?
发布是否不可追踪?

找到最耗时间的那一环,工程化工作通常就可以从那里开始。


下一篇准备聊聊前端依赖管理:package.json、锁文件和 pnpm 到底分别解决了什么,以及"我电脑可以运行"为什么不能算一个可靠结论。

相关推荐
花椒技术8 天前
Skill 落地系列一:Harness 试点复盘6 类真实业务场景的 Skill,全拆开给你看
agent·ai编程·前端工程化
Patrick_Wilson12 天前
从 webpack 到 Turbopack:讲透 monorepo 里的 transpilePackages
webpack·next.js·前端工程化
花椒技术13 天前
告别狂野式 AI Coding:Harness Engineering 在 H5 项目里的最小闭环实践
agent·ai编程·前端工程化
Patrick_Wilson13 天前
最佳实践是有保质期的:从一次 CDN external 白屏事故说起
前端·性能优化·前端工程化
gis开发之家14 天前
vue3组合式函数(Composables)的艺术——将业务逻辑从UI组件中连根拔起
前端·javascript·vue.js·前端框架·vue3·前端工程化·前端架构
至乐活着14 天前
前端工程化最佳实践:从规范到自动化构建的全面指南
webpack·自动化·代码规范·构建工具·前端工程化
逍遥运德16 天前
前端工程化-03:包管理NPM-package.json 和 package-lock.json 详解
前端·前端框架·前端工程化
HokKeung16 天前
Git Hooks 和 Husky 怎么选
前端·前端工程化
花椒技术19 天前
直播间常驻子应用加载优化实践:从 1550ms 到 890ms
性能优化·直播·前端工程化