JavaScript高级程序设计笔记 28

第 28 章 最佳实践

学习目标:从可维护性、性能和部署三个角度理解 JavaScript 项目工程实践,形成可长期维护、可优化、可上线的代码意识。

1. 本章核心脉络

JavaScript 不只是语法和 API。真实项目还要考虑代码是否容易维护、运行是否高效、上线过程是否可靠。本章把重点放在工程实践上:

  • 可维护性:代码清晰、规范、低耦合、容易修改。
  • 性能:减少不必要的计算、访问和 DOM 操作。
  • 部署:构建、验证、压缩和发布。

2. 可维护性

2.1 什么是可维护的代码

可维护代码通常具备:

  • 容易理解。
  • 命名清晰。
  • 结构一致。
  • 职责单一。
  • 注释恰当。
  • 低耦合。
  • 修改局部代码不容易影响全局。

判断标准不是“代码看起来高级”,而是团队成员在几个月后仍能快速理解并安全修改。

2.2 编码规范

编码规范让团队代码保持一致。

常见规范内容:

  • 缩进。
  • 变量命名。
  • 文件命名。
  • 注释风格。
  • 函数长度。
  • 模块组织。
  • 错误处理方式。
  • 类型和接口约定。

现代项目一般通过工具固化规范:

  • ESLint:代码质量检查。
  • Prettier:格式化。
  • TypeScript:类型约束。
  • EditorConfig:编辑器基础风格。

2.3 命名

命名要表达意图。

不推荐:

const d = new Date();
const arr = users.filter((x) => x.a);

更清晰:

const currentDate = new Date();
const activeUsers = users.filter((user) => user.isActive);

实践建议:

  • 布尔值使用 ishascanshould
  • 函数名使用动词或动宾结构。
  • 集合使用复数。
  • 避免无意义缩写。

2.4 注释

注释应该解释“为什么”,而不是重复“做了什么”。

好注释适合:

  • 解释复杂业务规则。
  • 说明兼容性处理原因。
  • 标注权衡和限制。
  • 记录非直觉实现的背景。

不好的注释:

// i 加 1
i++;

更有价值的注释:

// 服务端分页从 1 开始,组件内部页码从 0 开始。
const requestPage = currentPage + 1;

2.5 松散耦合

低耦合意味着模块之间依赖少、边界清晰。

常见方向:

  • JavaScript 与 HTML 解耦。
  • CSS 与 JavaScript 解耦。
  • 应用逻辑与事件处理解耦。
  • 数据层与视图层解耦。
  • 业务规则与工具函数解耦。

不推荐把行为写进 HTML:

<button onclick="save()">保存</button>

更推荐:

document.querySelector("#save").addEventListener("click", save);

2.6 编码惯例

实践建议:

  • 不修改不属于自己的对象,尤其是原生对象原型。
  • 避免全局变量。
  • 每个函数只做一件事。
  • 常量集中管理。
  • 对外暴露稳定接口。
  • 对错误路径有明确处理。
  • 删除无用代码,而不是长期注释。

3. 性能

3.1 性能优化原则

性能优化应基于测量,而不是猜测。

基本流程:

  1. 发现性能问题。
  2. 使用工具定位瓶颈。
  3. 做小范围优化。
  4. 再次测量验证结果。

常用工具:

  • Chrome DevTools Performance。
  • Lighthouse。
  • Network 面板。
  • Coverage。
  • Memory 面板。

3.2 作用域意识

变量查找会沿作用域链向外查找。现代引擎已经很强,但仍应保持代码结构清晰,避免不必要的全局访问和深层闭包滥用。

实践建议:

  • 局部变量缓存重复使用的值。
  • 避免在热路径中访问复杂链式属性。
  • 避免不必要的闭包保留大对象。
const { length } = items;

for (let i = 0; i < length; i++) {
  // ...
}

3.3 选择正确的方法

不同写法的性能和语义都可能不同。应先保证语义清晰,再关注性能。

常见建议:

  • 高频路径中避免重复创建对象和函数。
  • 大量字符串拼接可考虑数组或模板生成策略。
  • 需要快速查找时,用 MapSet 替代数组线性搜索。
  • 大数据列表渲染使用分页或虚拟列表。
  • 防抖和节流处理高频事件。

示例:

const selectedIds = new Set(ids);
const selectedUsers = users.filter((user) => selectedIds.has(user.id));

3.4 语句最少化

减少不必要语句可以降低执行成本,也能提升可读性。

常见手段:

  • 合并重复逻辑。
  • 避免重复条件判断。
  • 提前返回减少嵌套。
  • 把复杂逻辑拆成清晰函数。
function getDiscount(user) {
  if (!user.isMember) {
    return 0;
  }

  if (user.level === "vip") {
    return 0.2;
  }

  return 0.1;
}

3.5 优化 DOM 交互

DOM 操作通常比纯 JavaScript 计算更昂贵,尤其是频繁读写布局信息时。

实践建议:

  • 批量 DOM 更新。
  • 使用 DocumentFragment 或一次性字符串渲染。
  • 避免在循环中反复触发布局。
  • 缓存 DOM 查询结果。
  • 使用事件委托。
  • 对长列表使用虚拟滚动。
  • 用 CSS class 切换样式,而不是逐项修改内联样式。

事件委托:

list.addEventListener("click", (event) => {
  const button = event.target.closest("button[data-id]");

  if (!button) {
    return;
  }

  handleClick(button.dataset.id);
});

避免布局抖动:

const width = element.offsetWidth;

items.forEach((item) => {
  item.style.width = `${width}px`;
});

不要在循环中交替读取布局和写入样式。

4. 部署

4.1 构建流程

现代前端部署通常会经历:

  • 安装依赖。
  • 静态检查。
  • 单元测试。
  • 构建打包。
  • 资源压缩。
  • 文件指纹。
  • 上传 CDN 或服务器。
  • 发布验证。

常见构建工具:

  • Vite。
  • Webpack。
  • Rollup。
  • esbuild。
  • SWC。

4.2 验证

上线前应尽可能自动验证:

  • 语法检查。
  • 类型检查。
  • 单元测试。
  • 集成测试。
  • 构建是否成功。
  • 关键页面 smoke test。
  • bundle 体积检查。

示例 npm scripts:

{
  "scripts": {
    "lint": "eslint src",
    "typecheck": "tsc --noEmit",
    "test": "vitest",
    "build": "vite build"
  }
}

4.3 压缩

压缩可以减少网络传输体积。

常见处理:

  • 删除空白和注释。
  • 缩短局部变量名。
  • 移除不可达代码。
  • 合并和优化表达式。
  • Tree shaking。
  • gzip 或 Brotli 传输压缩。

注意:

  • 压缩代码要保留 source map,方便线上排错。
  • source map 是否公开要根据安全策略决定。
  • 文件名加内容 hash,便于长期缓存。

4.4 缓存与发布

静态资源常用策略:

  • JS/CSS 文件名带 hash,设置长期缓存。
  • HTML 不做强长期缓存,确保能拿到新资源入口。
  • CDN 刷新和回滚流程要清晰。
  • 重要发布要有监控和错误告警。

5. 工程实践建议

5.1 可维护性优先级

优先保证:

  • 代码能被团队理解。
  • 变更影响范围可控。
  • 错误能被发现。
  • 构建和发布可重复。

不要为了微小性能收益牺牲明显可读性,除非测量证明这是瓶颈。

5.2 性能优化优先级

通常优先关注:

  • 网络请求数量和体积。
  • 首屏渲染资源。
  • 大量 DOM 更新。
  • 长任务。
  • 内存泄漏。
  • 重复渲染。

5.3 部署可靠性优先级

上线链路应做到:

  • 可自动化。
  • 可验证。
  • 可回滚。
  • 可观测。

6. 面试高频问题

6.1 什么是可维护代码?

可维护代码是容易理解、容易修改、容易测试、行为可预测的代码。它通常命名清晰、职责单一、耦合低,并遵循统一规范。

6.2 如何减少 JavaScript 与 HTML 的耦合?

避免内联事件处理,把行为绑定放在 JavaScript 中,通过选择器、事件监听和组件接口建立关系。

6.3 前端性能优化从哪里入手?

先测量,再优化。通常从网络体积、首屏渲染、长任务、DOM 操作、重复计算和内存泄漏入手。

6.4 为什么 DOM 操作昂贵?

DOM 操作可能触发布局计算、样式重算和绘制。频繁读写布局信息还可能造成布局抖动,影响页面流畅度。

6.5 什么是 Tree shaking?

Tree shaking 是构建工具基于静态依赖分析移除未使用代码的优化。ES Module 的静态导入导出让它更容易实现。

6.6 为什么静态资源文件名要带 hash?

文件内容变化时 hash 变化,浏览器会请求新文件;内容不变时可以长期缓存。这能兼顾缓存效率和更新正确性。

7. 复习清单

8. 一句话总结

第 28 章的核心是把 JavaScript 写成可长期演进的软件:用规范和低耦合保证可维护性,用测量驱动性能优化,用自动化构建和验证保证部署可靠性。

posted @ 2025-09-16 11:16  Li_pk  阅读(5)  评论(0)    收藏  举报