Export & Import of ES6 Module
为什么不同形式的模块导入/导出会导致 Tree-shaking 效果不同?
Tree Shaking,从字面上解释就是把无效代码(dead code)抖掉,通常用于打包输出时,可以有效缩小打包体积。比如在开发时引入一个模块,上线打包时,理想目标是只把用到的部分打包进 bundle 里,其他未使用的部分不需要被打包进来。
在传统编译型的语言中,由编译器将 Dead Code 从 AST 中删除,dead code指:
对于 CommonJS 规范的模块,通过 require 动态引用模块,运行时才加载,不能通过简单的静态分析去做优化,这方面目前 Rollup 和 Parcel 的Tree Shaking支持更好。
对于 ES6 规范的模块,Tree Shaking 依赖于 import 和 export 的静态结构特性。
babel 插件在解析模块时,遇到 import 语句,找到对应路径的文件。但是这个文件可能是一个大的 package,并不是详细路径,需要继续遍历找到确切的需要被打包的代码文件。
#
export 导出#
命名导出ExportNamedDeclaration, 一个文件可以同时命名导出多个变量 / 函数 / 类,在其他文件导入时命名必须严格相同。
#
默认导出ExportDefaultDeclaration,一个文件只能默认导出一个变量 / 函数 / 类,在其他文件导入时可以使用其他命名。
#
重命名导出ExportNamedDeclaration, 导出时进行重命名,在其他文件导入时必须使用重命名后的名称。
重定向的命名并不能在本模块使用,只是搭建一个重定向的桥梁;如果需要在本模块内使用,则先导入、再导出,否则重复导入两次 Tree Shaking 在处理时会有问题。
#
模块重定向ExportNamedDeclaration or ExportAllDeclaration, 在当前模块中,导出指定导入模块的默认导出,相当于是创建了一个“重定向”。
通常在业务包的 index 文件里使用,这样我们在其他模块导入时可以直接写包路径,而不用写一长串的具体路径。
#
import 导入#
导入列表ImportSpecifier, 导入默认值
ImportDefaultSpecifier, 在目标文件是默认导出,导入时可以使用其他命名。
#
导入时重命名ImportSpecifier
#
导入整个模块的内容ImportNamespaceSpecifier, 全量引入,谨慎使用,打包体积可能会很大
#
动态导入在解析时,其他都是 ImportDeclaration,动态导入是 CallExpression
在遇到动态导入时,会把该模块单独打包得到一个 chunk,以实现按需加载(在使用时才导入)。
#
仅为副作用导入一个模块整个模块仅为副作用(中性词)而导入,将会运行模块中的全局代码, 但实际上不导入模块中的任何内容(接口)。
#
结论默认导出与导入默认值配对使用,为了避免命名冲突在导入时可以使用其他命名。为了更好的 Tree Shaking,from 后接详细路径。
命名导出与导入列表配对使用,在导入时严格使用相同的变量名(这个应该没问题,变量名不一致 IDE lint 会报错),可以重命名。
避免全量导入,尽量使用部分导入或导入列表。如果不得不使用全量导入,为了更好的 Tree Shaking,from 后接详细路径。
模块文件夹下的 index 文件可作为模块重定向的“桥梁”,避免详细路径过长的情况。
避免重复的导入。