总结一些在前端开发中代码体积的优化细节(不定期更新)

写在前面

首先本文内容基于几个前提:

  1. 项目至少存在前端工程化打包过程,如果是直接写 JS 上线请忽略。
  2. 仅限于代码中的一些体积优化,其他方面如网络传输、页面渲染性能等方面内容不在本文讨论范围。

正文

选择性使用 Optional Chaining

作为 ES 11 的一个新特性,浏览器产商跟进还需要一段时间,所以通常会通过 babel 转换成浏览器兼容的代码。例如:

1
2
// before
console.log(window.conf?.id)

babel 转换后:

1
2
3
// after babel
var _window$conf;
console.log((_window$conf = window.conf) === null || _window$conf === void 0 ? void 0 : _window$conf.id);

简化缩小后:

1
2
// minify (with mangle)
var o;console.log(null===(o=window.conf)||void 0===o?void 0:o.id)

可以看到即使是简化缩小后,代码量仍然增加了数倍。所以部分情况下还是可以考虑使用原来的方式:

1
console.log(window.conf && window.conf.id)

当然,请不要生搬硬套,使用哪种方式取决于多方面因素,主要是代码体积和代码简洁和可读性的取舍。

重复使用的属性单独用变量保存

主要是为了配合一些 minifier 工具的 mangle 功能,例如一个处理 input[type="file"] onChange 事件的函数:

1
2
3
4
5
6
7
8
/** @type {(evt: Event) => void} */
const handleFileInputChange = evt => {
if (!evt.target || !(evt.target instanceof HTMLInputElement) || !evt.target.files || !evt.target.files.length) return

const file = evt.target.files[0]
console.log(file)
// Do something with File Object ...
}

打包后的结果为(153 character):

1
const t=t=>{if(!(t.target&&t.target instanceof HTMLInputElement&&t.target.files&&t.target.files.length))return;const e=t.target.files[0];console.log(e)};

但其实我们可以和 minifier 配合得到更小体积代码,比如这么写:

1
2
3
4
5
6
7
8
9
/** @type {Event => void} evt */
const handleFileInputChange = evt => {
const target = evt.target
if (!target || !(target instanceof HTMLInputElement) || !target.files || !target.files.length) return

const file = target.files[0]
console.log(file)
// Do something with File Object ...
}

结果(135 character):

1
const e=e=>{const n=e.target;if(!(n&&n instanceof HTMLInputElement&&n.files&&n.files.length))return;const t=n.files[0];console.log(t)};

这里只是一个很小的 case 所以体现不出太大差异。但建议保持良好的编码习惯,在实际项目中更复杂的场景中,可以很好的优化我们的代码体积。

如非必要不要访问 process

经过测试发现,一旦在代码中访问 process 变量后,整个 NodeJs processenv argv on 等很多属性和方法的 polyfill 都会添加到构建包中。

所以如非必要尽量避免在代码中任何位置访问 process

这部分只进行了简单测试,后续有时间再稍稍深入研究补充一下。