开发服务器能打开页面,只说明当前开发路径成立。生产产物还会经过类型检查、模块转换、资源处理与部署。把这些步骤分清,才能定位“本地正常,发布后失败”的原因。

建立能复现的工程

包清单表达直接依赖,锁文件记录解析结果。应用项目应提交锁文件,CI 使用相应包管理器的冻结安装方式。不要混用多个锁文件后只凭本机缓存判断可复现。Node 版本必须满足所用工具要求,可在项目中记录并在 CI 核对。

Vite 等工具的开发模式与生产构建不同;TypeScript 转换未必等于完整类型检查,因此单独运行 tsc 或对应框架检查器。环境变量如果进入客户端构建,用户通常可以读取;变量名有前缀并不等于被加密。

模块设计优先保持显式依赖与清晰入口。动态 import 可以形成按需加载边界,但细碎拆分会增加加载与调度成本。分析真实产物后再决定拆分,不能只凭源码文件数量判断包体。

不同检查保护不同失败

示例模型 与测试 不依赖 UI 框架,可在同一目录运行 node –test model.test.mjs。它检查不可变更新、未知 ID 和重复操作,而不是截图像不像预期。

import test from "node:test";
import assert from "node:assert/strict";
import { setDone } from "./model.mjs";

test("完成操作不修改原始记录", () => {
  const before = [{ id: "html", done: false }];
  const after = setDone(before, "html", true);
  assert.equal(before[0].done, false);
  assert.equal(after[0].done, true);
});

类型检查验证表达的约束,单元测试验证纯规则,组件测试验证输入与事件,端到端测试验证真实路由、存储与关键任务。不要把每一种组合都放进耗时的浏览器测试;登录、保存、刷新恢复等关键路径更值得保护。

浏览器测试优先按角色和可访问名称定位,等待可观察结果,不写固定 sleep 掩盖竞争。网络桩能隔离后端波动,同时仍需少量真实接口契约检查。测量性能时使用生产构建;开发调试开销会影响结论。

发布也要验证输入和回退

CI 可以依次执行安装、类型检查、规则检查、测试、构建和产物检查。具体项目按失败成本安排并行。发布记录应能对应源代码版本、依赖与配置;保留可回滚产物,避免构建时意外读取未记录的本地文件。

静态资源常用带内容指纹的文件名;HTML 与数据更新要避免引用已被删除的旧资源。检查根路径与子路径部署、刷新深链接、缓存更新和无网络反馈。source map 有利于定位生产错误,但公开方式要符合源代码和隐私要求。

练习:故意写一个类型错误,确认开发服务器可运行与类型检查是否有不同结果。再给未知 ID 操作添加测试,说明预期是拒绝、忽略还是上报,并让实现遵守约定。

参考:Vite 指南、Node 测试运行器、Playwright 断言。