开发服务器能打开页面,只说明当前开发路径成立。生产产物还会经过类型检查、模块转换、资源处理与部署。把这些步骤分清,才能定位“本地正常,发布后失败”的原因。
建立能复现的工程
包清单表达直接依赖,锁文件记录解析结果。应用项目应提交锁文件,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 操作添加测试,说明预期是拒绝、忽略还是上报,并让实现遵守约定。