C05 JavaScript 工程化

2026-10-31 17:050 阅读约 13 分钟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 验证与讨论

  1. 在 tooling 目录执行 npm install,确认依赖安装完成且生成锁文件。
  2. 执行 npm run lint,确认无输出;把 const 改为 let 后再次执行,确认报 prefer-const。
  3. 执行 npm test,确认两个用例通过;把期望值改为 ¥1,299.5,确认断言失败并给出差异。
  4. 执行 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

参考文献

[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

0/2000

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