C05 JavaScript 工程化
C05 JavaScript 工程化
摘要:工程化解决四件事:依赖能声明、代码能检查、产物能压缩、线上错误能映射回源码。ESM 声明依赖,ESLint 与 JSDoc 类型检查在提交前拦截缺陷,打包工具把多文件合成少量产物并生成 sourcemap,测试覆盖核心纯函数。本文给出从零搭建的最小工程,含五个配置文件与一条构建命令。
关键词:工程化,ESLint,JSDoc,打包,sourcemap
所属:frontend/js / C-高级 | 上一篇:C04-并发控制与取消 | 下一篇:C06-深拷贝与结构化克隆 | 返回:C00-总结
1 引言
十几个模块直接在浏览器里加载,首屏要发起十几次请求;团队成员缩进风格各异,改动互相覆盖;线上报错堆栈指向压缩后的单行文件,无法定位。
本文回答四个问题:依赖怎么声明、静态检查怎么配、产物怎么生成、错误怎么回溯。范围见表 1-1。
| 项 | 本文覆盖 | 本文不覆盖 |
|---|---|---|
| 依赖 | package.json、ESM、锁文件 |
私有仓库与发布 |
| 检查 | ESLint、JSDoc 类型检查 | TypeScript 完整类型系统 |
| 构建 | esbuild 打包、压缩、sourcemap | 多入口分包策略 |
| 测试 | Node.js 内置测试运行器 | 端到端测试 |
2 预备知识
| 项 | 要求 | 核对方式 |
|---|---|---|
| 环境 | Node.js 20+、npm 10+ | node -v、npm -v |
| 前置 | B05 | 会写 ESM 模块 |
| 目录 | 新建 js-practice/tooling/ |
独立于其它示例 |
3 原理与实现
3.1 构建流水线
源码经静态检查后进入打包,产出带哈希的文件与 sourcemap,如图 3-1 所示。
flowchart LR
A["src/*.mjs"] --> B["ESLint 与 JSDoc 检查"]
B -->|通过| C["esbuild 打包压缩"]
B -->|不通过| D["中断并报错"]
C --> E["dist/main.js"]
C --> F["dist/main.js.map"]
E --> G["浏览器执行"]
F --> H["线上错误映射回源码行"]
图 3-1 检查在打包之前,sourcemap 随产物同步发布
图上可见:检查失败不产出文件,产物与 sourcemap 必须成对发布,否则线上堆栈无法还原。
3.2 最小工程配置
// js-practice/tooling/package.json;Node.js 20+
{
"name": "js-practice-tooling",
"version": "1.0.0",
"type": "module",
"scripts": {
"lint": "eslint src",
"check": "tsc --noEmit",
"test": "node --test test/",
"build": "esbuild src/main.mjs --bundle --minify --sourcemap --format=esm --outfile=dist/main.js"
},
"devDependencies": {
"esbuild": "^0.24.0",
"eslint": "^9.0.0",
"typescript": "^5.6.0"
}
}
// js-practice/tooling/src/format.mjs;运行环境:Node.js 20+
/**
* 把金额格式化为人民币字符串。
* @param {number} amount 金额,单位元
* @returns {string} 形如 "¥1,299.50"
*/
export function money(amount) {
if (!Number.isFinite(amount)) throw new TypeError('amount 必须是有限数字');
return new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' }).format(amount);
}
// js-practice/tooling/src/main.mjs;运行环境:Node.js 20+
import { money } from './format.mjs';
document.addEventListener('DOMContentLoaded', () => {
const el = document.querySelector('#total');
if (el) el.textContent = money(1299.5);
});
// js-practice/tooling/tsconfig.json;TypeScript 5.6;只做类型检查,不产出文件
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"checkJs": true,
"allowJs": true,
"strict": true,
"noEmit": true,
"skipLibCheck": true
},
"include": ["src/**/*.mjs"]
}
// js-practice/tooling/eslint.config.mjs;ESLint 9 扁平配置
export default [
{
files: ['src/**/*.mjs'],
languageOptions: {
ecmaVersion: 2022,
sourceType: 'module',
globals: { document: 'readonly', Intl: 'readonly' },
},
rules: {
'no-unused-vars': 'error',
'no-undef': 'error',
eqeqeq: ['error', 'always'],
'prefer-const': 'error',
},
},
];
// js-practice/tooling/test/format.test.mjs;运行环境:Node.js 20+ 内置测试运行器
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { money } from '../src/format.mjs';
test('格式化人民币金额', () => {
assert.equal(money(1299.5), '¥1,299.50');
});
test('非数字抛 TypeError', () => {
assert.throws(() => money('abc'), TypeError);
});
命令与作用见表 3-1。
| 命令 | 作用 | 失败表现 |
|---|---|---|
npm run lint |
检查语法与风格 | 输出文件行号与规则名 |
npm run check |
用 JSDoc 推导类型 | 输出类型错误与位置 |
npm test |
运行单元测试 | 输出失败用例与断言差异 |
npm run build |
打包压缩并生成 sourcemap | 输出语法错误位置 |
工程化四层职责见表 3-2。
| 层 | 工具 | 拦截的问题 |
|---|---|---|
| 编辑期 | 编辑器插件 | 语法错误、未定义变量 |
| 提交前 | ESLint、类型检查 | 风格不一致、类型不符 |
| 构建期 | 打包器 | 路径错误、产物过大 |
| 运行期 | sourcemap、日志 | 线上异常定位 |
4 验证与讨论
- 在
tooling目录执行npm install,确认依赖安装完成且生成锁文件。 - 执行
npm run lint,确认无输出;把const改为let后再次执行,确认报prefer-const。 - 执行
npm test,确认两个用例通过;把期望值改为¥1,299.5,确认断言失败并给出差异。 - 执行
npm run build,确认dist/下同时生成main.js与main.js.map。
| 检查项 | 预期结果 | 说明 |
|---|---|---|
| 依赖声明 | 锁文件生成 | 版本可复现 |
| 静态检查 | 违规即报错退出 | 退出码非 0 可挂 CI |
| 类型检查 | JSDoc 标注生效 | checkJs 打开 |
| 产物完整 | .js 与 .map 成对 |
缺少 map 无法还原堆栈 |
讨论与局限。 JSDoc 类型检查能覆盖多数标注,但泛型与条件类型表达力不及 TypeScript,复杂库仍需 .d.ts。另,--minify 会改变函数名,依赖函数名的逻辑(如 constructor.name)在压缩后失效。
5 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
npm run lint 报找不到配置 |
配置文件未按 ESLint 9 格式命名 | 用 eslint.config.mjs |
类型检查报 Cannot find name document |
缺少 DOM 类型库 | 安装 @types/node 或在 lib 中声明 |
产物运行报 exports is not defined |
误用了 CommonJS 格式 | 指定 --format=esm |
| 线上堆栈仍指向压缩代码 | sourcemap 未上传或未开启 | 确认 --sourcemap 且随产物发布 |
| 构建通过但页面白屏 | 入口路径或 DOM 选择器写错 | 打开控制台看首个错误 |
6 结论
| 序号 | 结论 | 依据 |
|---|---|---|
| 1 | 检查在打包之前,失败即中断 | 图 3-1 |
| 2 | 依赖声明与锁文件保证环境可复现 | 表 3-1 |
| 3 | JSDoc 加 checkJs 可在不引入 TS 的前提下做类型检查 |
3.2 节 |
| 4 | sourcemap 必须与产物成对发布 | 表 3-2 |
- 上一篇:C04-并发控制与取消
- 下一篇:C06-深拷贝与结构化克隆
- 返回:C00-总结 | 科目 README
参考文献
[1] esbuild · 构建配置文档,https://esbuild.github.io/api/(访问于 2026-10-31)
[2] ESLint · 配置文件参考,https://eslint.org/docs/latest/use/configure/configuration-files(访问于 2026-10-31)
[3] Node.js · Test runner,https://nodejs.org/api/test.html(访问于 2026-10-31)

评论 0
还没有评论,快来抢沙发吧~