require()、import、import()加载模块详解(二)

接上篇 require()、import、import()加载模块详解(一)

ES6 Module 的 import

通过 import 静态地导入另一个通过 export 导出的模块。

区分于 CJS 运行时才和导入模块建立关系,ESM 在转化成中间代码时(编译阶段) import 语句就和模块建立了静态引用关系,在运行时导入和导出是不可更改的。这就意味着我们只能在顶层进行导入和导出 (比如绝不能嵌套在条件语句中),同时 import 和 export 语句不能有「具有逻辑或含有变量」的动态部分,即不能依赖于运行时计算的任何内容 (如import foo from './module' + 变量;),不然编译时就会报错。而 require 可以在运行时通过 if 判断决定导入哪个模块。

在编译期,import 语句会被内部移动至当前作用域最开头 (类似 var 和 function 的变量提升),先于其他代码执行。JS 解析器编译到 import 语句时,会生成一个接口标识符或默认导出接口对应的引用。如 import { a } from './module-a',a 指向的是export const a = xxx 接口中的 a;而 import defaultB from './module-b',defaultB 指向的是 export default b 中的 b (默认接口导入时的名称可以自定义)。到了运行期,也不会去执行完整模块,只有在调用 a / defaultB 的时候才会加载模块中相应的接口取值。

换句话说,ESM模块规则有点像Unix系统的“符号连接”,原始值变了,import 输入的值也会跟着变。导入的变量绑定其所在的模块,不会缓存值。不同脚本加载同一个模块得到的是同一个实例。因此ESM设定了不能修改导入值的只读规则。

CJS 导入的是导出值的浅拷贝副本,而ESM导入是导出值的实时只读引用。

静态型的 import 是初始化加载依赖项的最优选择, 静态模块结构 更容易从代码静态分析工具和 tree shaking 中受益。而且自动支持模块间的循环依赖。
在用 webpack、Rollup 这样的模块打包器时,证明ESM模块可以更高效地组合:

  • 加载所有模块时,import 查找变量是静态检索,比 require() 的动态检索快很多。
  • 压缩绑定的文件比压缩单独的文件效率更高。
  • 在绑定过程中,通过删除未使用的出口代码,从而节省大量空间。

在浏览器中,import 语句只能在 标签中使用 (

  • 自动支持模块之间的循环依赖关系
  • 尽管ESM模块规范大有优势,但鉴于很多库还在广泛使用CJS,我们仍需要理解requiremodule.exports/exports。自己在日常开发中使用importexport default/export即可,webpack 会帮你做兼容处理 (可以看到webpack自身是遵循CJS的,因此会在打包过程中先把esm转成cjs) 。期待全面支持ESM的一天~

    参考:es6 modules

    你可能感兴趣的:(require()、import、import()加载模块详解(二))