网站首页 > 技术文章 正文
大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!
1. 通用框架或者库的代码体积危机
前端开源框架或者库为了提升开发者的 DX 而采用的一种常见模式是使用单一入口文件来重新导出所有公共 API。然而,该方式会产生一个潜在的问题,即导致大量未使用的代码被包含在模块图谱 (Module Graph) 中。
// 统一入口文件 lodash.js
export {default as add} from "./add.js";
export {default as divide} from "./divide.js";
export {default as debounce} from "./debounce.js";
export {default as map} from "./map.js";
虽然可以使用称为 “tree-shaking” 的技术来解决此类问题,即跟踪模块导出的各个绑定的依赖关系,并移除那些未使用的重新导出。
// tree-shaking 会保证只导入并使用 add 和 multiply 函数
import {add, multiply} from './math';
console.log('Add:', add(2, 3));
// 输出: Add: 5
console.log('Multiply:', multiply(4, 5));
// 输出: Multiply: 20
然而,由于模块加载可能带来副作用,该技术并非总是可行,例如:不同的工具在 代码大小和正确性之间会做出权衡,从而导致 Web 应用程序优化不足,或者由于非纯模块 (non-pure module) 未按预期执行而导致难以调试。
// 非纯模块,其在加载时直接修改了全局状态
console.log('Non-pure module loaded!');
// 修改全局变量
window.someGlobalVariable = 'Modified by nonPureModule';
// 导出一个函数
export function doSomething() {
console.log('Doing something...');
}
同时,当直接在浏览器中运行 ESM 时,tree-shaking 技术也存在不足,因为其默认需要全程序分析 (whole-program analysis)。而浏览器不会对整个程序进行静态分析 ,因为其只负责加载和执行模块,而不关心模块之间的依赖关系或未使用的代码。
2. 为什么需要延迟重新导出 (Deferred re-exports)
实际上,Web 应用通常包含大量 JavaScript 代码,从而对启动时间产生重大影响。一种可行的方法是加载尽可能少的必要代码,并预加载将来可能需要的代码。然而,该策略在实践中很难实现,常常导致 Web 应用程序优化不足。
导入延迟提案 (import defer proposal) 解决了部分问题,其允许以最小的代价延迟执行应用程序启动期间不需要的代码。例如:
// 该提案会加载./helpers.js 和其依赖,但是不会立即执行
import defer * as helpers from "./helpers.js";
function fn() {
// helpers.js 仅在此时会执行
helpers.doSomething();
}
延迟重新导出提案通过允许库将重新导出 (reexport) 标记为 “未使用则忽略” ,最终解决了通用前端框架或者库的体积危机问题。其实现了以下核心目的:
- 遵循清晰的语义而非依赖工具定义的启发式方法
- 原生 JS 平台也可以实现这些语义以避免加载未使用的代码
- 与 import defer 提案集成,使重新导出 (reexport) 能够受益于相同的 “加载后,仅在实际需要时执行” 语义
延迟重新导出可以与 import defer 提案结合使用:
// 模块 math.js
export defer {add} from "./math/add.js";
export defer {sub} from "./math/sub.js";
当模块使用 import {add} from "./math.js"; 导入时,其会加载并执行./math.js 和 ./math/add.js,同时跳过 ./math/sub.js 及其所有依赖项。
3. 延迟重新导出模块的执行顺序
延迟重新导出的模块会在重新导出它们的模块之后执行,且按照重新导出的顺序执行,比如下面的示例:
// 重新导出模块 barrel.js
export defer {a} from "./a.js";
export {b} from "./b.js";
export defer {c} from "./c.js";
export {d} from "./d.js";
export defer {e} from "./e.js";
// 这里是入口导入文件 entrypoint.js
import {e, a, d} from "./barrel.js";
此时模块执行顺序为:
- b.js
- d.js
- barrel.js
- a.js
- c.js
- e.js
- entrypoint.js
与按源代码顺序执行所需内容相比,始终在重新导出它们的模块之后执行延迟导出的模块 ,可以提高不同类型的模块图谱之间的一致性。
4. 延迟重新导出与 import defer 集成
import defer 提案规定,使用命名空间导入时,defer 关键字表示 “仅在实际需要时执行此模块”。对于模块命名空间对象,export defer 也遵循类似的语义:
// 模块 math.js
export defer {add} from "./math/add.js";
export defer {sub} from "./math/sub.js";
export {mul} from "./math/mul.js";
// 模块 index.js
import * as math from "./math.js";
// 所有方法都加载, 会执行 ./math.js 和 ./math/mul.js
math.add;
// 执行 ./math/add.js
math.sub;
// 执行 ./math/sub.js
在将 export defer 与 import defer 配对使用时,可以提供更多的控制:
// 模块 math2.js
export defer {add} from "./math/add.js";
export defer {sub} from "./math/sub.js";
// 模块 index2.js
import defer * as math from "./math2.js";
// 所有方法加载,但是任何方法都不会执行
math.add;
// 执行 ./math.js ./math/add.js
math.sub;
// 执行 ./math/sub.js
参考资料
https://github.com/tc39/proposal-deferred-reexports
https://github.com/tc39/proposal-defer-import-eval/
https://www.youtube.com/watch?v=0t-Le4kdaMg
猜你喜欢
- 2025-05-15 宇宙厂:深入聊聊 CJS 和 ESM 模块化三点核心差异?
- 2025-05-15 #前端高手进阶#一起薅羊毛~
- 2025-05-15 前端基础进阶(十):深入详解函数的柯里化
- 2025-05-15 2025 年 Object 和 Map 如何选择?
- 2025-05-15 为何说 postMessage 才是真正的 setTimeout(0)?
- 2025-05-15 为什么高手写 JS 总是又快又好?这10个技巧你要知道
- 2025-05-15 2025 年 Deno 终于官宣 pnpm 和 Yarn 可使用 JSR?
- 2025-05-15 宇宙厂:为什么前端要了解 Interaction to Next Paint (INP)
- 2025-05-15 Node.js 原生支持 TypeScript?开发者需要了解的一切
- 2025-05-15 请务必用 postTask/isInputPending 释放JS主线程!
你 发表评论:
欢迎- 05-24网络信息安全之敏感信息在传输、显示时如何加密和脱敏处理
- 05-24常见加密方式及Python实现
- 05-24pdf怎么加密
- 05-24aes256 加密 解密 (python3) 「二」
- 05-24深入理解Python3密码学:详解PyCrypto库加密、解密与数字签名
- 05-24Springboot实现对配置文件中的明文密码加密
- 05-24JavaScript常规加密技术
- 05-24信息安全人人平等 谷歌推出低性能安卓手机加密技术
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端md5加密 (49)
- 前端路由 (55)
- 前端数组 (65)
- 前端定时器 (47)
- 前端接口 (46)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle链接 (47)
- oracle的函数 (57)
- mac oracle (47)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)