最小化 JavaScript 包大小的实用技巧
作品:https://code-art.pictures/
为什么要麻烦?
现在可能会令人惊讶,但在许多情况下互联网流量仍然是一个问题。移动网络的数据套餐通常有限,设备电池也不是无限的,最重要的是,用户在等待网站加载时的注意力也是有限的。这就是为什么捆绑包大小仍然很重要。这里有七个建议供您考虑。
1. 不要转换为 ES5
2020 年,我正在为本地社交网络维护一个促销应用程序。这是一个典型的针对 ES5 的 React TypeScript Webpack 应用程序。当 webpack 5 发布时,我决定升级。一切都很顺利;我监控了错误分析和用户反馈,没有什么意外的。一周后,我意外地发现我的包中包含了箭头函数——这是一个新的 webpack 功能。
这是一篇关于 ES5 状态的精彩文章。要点:
- 许多库已经包含 ES6 代码,这意味着它们的捆绑包不兼容 ES5。
- 世界上大多数流行的网站都不兼容 ES5 — 您的网站可能也不需要它。
- 如果您确定仍然需要 ES5 兼容性,则必须在构建过程中包含这些库。
2.了解并使用现代 JavaScript 语言特性
这里有一些功能,可以让您编写更好、更紧凑的代码。
2.1.发电机
生成器是遍历嵌套结构的有效方法:
type TreeNode<T> = { left?: TreeNode<T> value: T right?: TreeNode<T> }; function* traverse<T>(root: TreeNode<T>): Generator<T> { if (root.left) yield* traverse(root.left) yield root.value if (root.right) yield* traverse(root.right) }
2.2.私有类字段
压缩器确信这些字段不能有外部用途,即使在导出的对象中也是如此,并且可以自由缩短它们的名称。
来源
export class A { #myFancyStateObject }
捆绑包
export class A{#t}
当然,对于 TypeScript 私有字段来说,这不起作用,因为一旦 tsc 完成其工作,它们是私有的这一知识就会消失。
2.3.现代 API
你听说过 Promise.withResolvers() 或 Map.groupBy() 吗?在撰写本文时,这些 API 尚未广泛使用,但很快就会广泛使用。现在花点时间熟悉它们,并准备好在几年后采用它们。
提示:如何发现新的 JavaScript API
有无数的博客和播客,但我发现最好的“新闻通讯”是 TypeScript 存储库中的新 .d.ts 文件。例如,只需打开 es2024.collection.d.ts 即可享受?
3. 避免代码重复
你注意到重复的模式了吗?
type TreeNode<T> = { left?: TreeNode<T> value: T right?: TreeNode<T> }; function* traverse<T>(root: TreeNode<T>): Generator<T> { if (root.left) yield* traverse(root.left) yield root.value if (root.right) yield* traverse(root.right) }
重复的代码不仅增加了包的大小,而且还使理解每个部分的作用变得更加困难。这通常会导致开发人员编写新代码,而不是识别和重用现有的实用函数,从而进一步使捆绑包变得臃肿。
关于这个主题已经有很多优秀的材料,所以我不再重述它,而是推荐经典:Martin Fowler 的重构。它不仅涵盖了上面的简单示例,还涵盖了耦合层次结构和重复设计等复杂情况。
现在,让我们改进我们的小例子。看来clamp经常用于将参数限制在数组索引范围内,所以我们可以创建一个快捷方式:
export class A { #myFancyStateObject }
此更改明确表明 n 可能是一个整数,目前尚未检查。它还突出显示了一个未处理的边缘情况:空数组。通过进行这个小的重复数据删除,我们还发现了两个潜在的错误?
4.避免过度设计
我不记得这句话的确切来源,但我认为它是正确的:
过度设计正在解决你没有的问题。
在 Web 开发领域,我观察到两种主要类型的过度设计。
4.1.过度概括
考虑这段代码。内边距是 4px 的倍数,背景颜色是蓝色阴影。这可能不是巧合,如果是这样,则可能表明可能存在重复。但是我们真的有足够的信息来提取通用 Button 组件,还是我们过度设计了?
CSS
export class A{#t}
JSX
const clamp = (min, val, max) => Math.max(min, Math.min(val, max)) const x = clamp(0, v1, a.length - 1) const y = clamp(0, v2, b.length - 1) const z = clamp(0, v3, c.length - 1)
这个建议确实与“避免重复”有些冲突。过度重复代码删除可能会导致过度设计。那么,你在哪里划清界限呢?就我个人而言,我使用神奇的数字“3”:一旦我看到三个具有相似模式的地方,可能是时候提取通用组件了。
对于我们的蓝色按钮,我相信最好的解决方案是使用 CSS 变量,至少用于填充,而不是创建一个新组件。
4.2.使用不正确的框架
是的,我说的是我们喜欢的东西——Next.js、React、Vue 等等。如果您的应用程序在 DOM 元素级别不涉及大量交互性,或者不是动态的,或者非常简单,请考虑其他选项:
- 静态站点生成器 - 您可以从一些精选列表开始。
- 注意:其中一些在幕后使用 React 或其他框架。如果您的目标是捆绑最小化,请尝试不同的方法。
- 内容管理系统,例如 WordPress。
- Vanilla — 在两种情况下特别有用:
- 该应用程序非常简单。
- 该应用程序不会操作太多 DOM,而是在画布上绘制一些东西。我有一个与此完全相同的项目。
5. 避免过时的 TypeScript 功能
TypeScript 当前的目标主要是对 JavaScript 进行类型检查,但情况并非总是如此。早在 ES6 出现之前,人们就曾多次尝试创建“更好的 JavaScript”,TypeScript 也不例外。有些功能可以追溯到早期。
5.1.枚举
它们不仅难以正确使用,而且还会转换成相当冗长的结构:
TypeScript
type TreeNode<T> = { left?: TreeNode<T> value: T right?: TreeNode<T> }; function* traverse<T>(root: TreeNode<T>): Generator<T> { if (root.left) yield* traverse(root.left) yield root.value if (root.right) yield* traverse(root.right) }
JavaScript
export class A { #myFancyStateObject }
官方 TypeScript 手册建议使用简单对象而不是枚举。您也可以考虑联合类型。
5.2.命名空间
命名空间是 ESM 之前的模块解决方案。它们不仅增加了包的大小,而且由于命名空间是全局的,因此在大型项目中很难避免命名冲突。
TypeScript
export class A{#t}
JavaScript
const clamp = (min, val, max) => Math.max(min, Math.min(val, max)) const x = clamp(0, v1, a.length - 1) const y = clamp(0, v2, b.length - 1) const z = clamp(0, v3, c.length - 1)
使用 ES 模块代替命名空间。
注意:但是,对于为全局库编写类型定义,命名空间仍然有用。
6. 不要忽视小的优化
这些小技巧中的每一个都可以为您节省捆绑中的几个到几十个字节。如果坚持使用,可以带来明显的效果。
6.1.使用真/假属性
例如,空字符串是假的。要检查它是否已定义且非空,您可以简单地编写:
const clampToRange = (n, {length}) => clamp(0, n, length - 1) const x = clampToRange(v1, a) // ...
6.2.有时允许非严格比较
我相信使用 == 强制 null 为 undefined,反之亦然,是完全合理的。
.btn-a { background-color: skyblue; padding: 4px; } .btn-b { background-color: deepskyblue; padding: 8px; }
6.3.使用空合并、逻辑或和默认参数来替换默认值
<button className='btn-a' onClick={handleClick}> Show </button> // ... <button className='btn-b' onClick={handleSubmit}> Submit </button>
6.4.使用单行箭头函数
而不是这个:
enum A { x, y }
写下:
var A; (function (A) { A[A["x"] = 0] = "x"; A[A["y"] = 1] = "y"; })(A || (A = {}));
6.5.不要对非常简单的对象使用类
而不是这个:
namespace A { export let x = 1 }
写下:
var A; (function (A) { A.x = 1; })(A || (A = {}));
您还可以冻结对象以保护其属性免遭更改。
7.定期检查捆绑包
对于每个捆绑器,都有可视化其内容的工具,例如用于 webpack 的 webpack-bundle-analyzer 和用于 Vite 的 vite-bundle-analyzer。这些工具可以帮助您识别捆绑包的常见问题:
- 库占用了不成比例的空间 — 也许是时候迁移或升级了?
- 项目的不同部分使用了两个相似的库 - 您可以合并并仅使用一个吗?
- 您的捆绑包中存在一个大文件,但只能由 0.5% 用户访问的页面访问(例如许可证文本)——也许您可以使用动态 import() 对捆绑包进行分区?
除了这些工具之外,偶尔手动阅读捆绑包以发现违规行为也是一个好主意。例如,由于 TypeScript 配置错误,您可能会在 ES6 捆绑包中找到 ES5 帮助程序,或在 ESM 项目中找到 CJS 帮助程序。这些问题可能无法被自动化工具发现,但仍然会增加加载时间,并可能会损失您最宝贵的资产 - 用户的注意力。
感谢您的阅读。快乐编码!
以上是最小化 JavaScript 包大小的实用技巧的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

JavaScript是现代Web开发的基石,它的主要功能包括事件驱动编程、动态内容生成和异步编程。1)事件驱动编程允许网页根据用户操作动态变化。2)动态内容生成使得页面内容可以根据条件调整。3)异步编程确保用户界面不被阻塞。JavaScript广泛应用于网页交互、单页面应用和服务器端开发,极大地提升了用户体验和跨平台开发的灵活性。

Python和JavaScript开发者的薪资没有绝对的高低,具体取决于技能和行业需求。1.Python在数据科学和机器学习领域可能薪资更高。2.JavaScript在前端和全栈开发中需求大,薪资也可观。3.影响因素包括经验、地理位置、公司规模和特定技能。

学习JavaScript不难,但有挑战。1)理解基础概念如变量、数据类型、函数等。2)掌握异步编程,通过事件循环实现。3)使用DOM操作和Promise处理异步请求。4)避免常见错误,使用调试技巧。5)优化性能,遵循最佳实践。

如何在JavaScript中将具有相同ID的数组元素合并到一个对象中?在处理数据时,我们常常会遇到需要将具有相同ID�...

实现视差滚动和元素动画效果的探讨本文将探讨如何实现类似资生堂官网(https://www.shiseido.co.jp/sb/wonderland/)中�...

JavaScript的最新趋势包括TypeScript的崛起、现代框架和库的流行以及WebAssembly的应用。未来前景涵盖更强大的类型系统、服务器端JavaScript的发展、人工智能和机器学习的扩展以及物联网和边缘计算的潜力。

深入探讨console.log输出差异的根源本文将分析一段代码中console.log函数输出结果的差异,并解释其背后的原因。�...
