模块化代码-所有代码都可以按需彼此访问并高效加载。
基础知识
每一个 ES6 模块都是一个包含 JS 代码的文件,模块本质上就是一段脚本,而不是用 module 关键字定义一个模块,但是模块与脚本还是有两点区别:
- 在模块中你可以使用 import 和 export 关键字。
- 在 ES6 模块中,无论你是否加入“use strict;”语句,默认情况下模块都是在严格模式下运行。
导出模块到其他模块中最简单的是使用export:
可以导出所有的最外层函数、 类以及 var、 let 或 const 声明的变量。代码就是模块,不是一段脚本,所以所有的声明都被限定在模块的作用域中,对所有脚本和模块全局不可见。你需要做的是将组成模块公共 API 的声明全部导出。
在模块中,除 export 之外的代码无异于普通代码,你可以访问类似 Object 和 Array这样的全局对象。
引用模块 import
导入多个模块:
运行的模块中包含一条 import 声明时,首先会加载被导入的模块;然后依赖图的深度优先遍历按顺序执行每一个模块的主体代码;为了避免形成回环,所有已执行的模块都会被忽略。
Export列表
在花括号中按照列表的格式写下你想导出的所有名称
export 列表可以在模块文件最外层作用域的每一处声明,不一定非要把它放在模块文件的首行。你也可以声明多个 export 列表,甚至通过其它的 export 声明打造一个混合的 export 列表,只要保证每一个被导出的名称是唯一的即可。
重命名 import 和 export
若导出的名字与其他名称冲突,可以使用重命名解决问题
导出的时候也可以重命名
Default Exports
目前使用的模块系统有Common.js 和AMD.js .ES6支持与这两个模块的交互,如果有Node项目,并且已经执行 npm install lodash 可以从lodash中导入独立的函数
另一种方式导入:_each.();用 require()加载这些模块也会得到相同的结果——exports 对象。
同时ES6不止可以导出CommonJS的包,也可以导出想要的内容:例如导出非ES6包的colors它是诸多CommonJS模块的集合
默认导出与其他导出类似,唯一不用就是它名叫“default”
模块对象
import *时,导入的其实是一个模块命名空间对象,模块将它的所有属性都导出了。所以如果“cows”模块导出一个名为 moon()的函数,然后用上面这种方法“cows”将其全部导入后,你就可以这样调用函数了: cows.moo()。VUE中使用这样的用法
聚合模块
有时候为了简化代码,可以用统一的方式将其他模块中的内容聚合在一起导出,
使用export * 方法容易与其他命名冲突,使用时需要注意。
Import实际做了什么
JS引擎加载模块会按照以下步骤执行:
- 语法解析:阅读模块源代码,检查语法错误。
- 加载:递归地加载所有被导入的模块。这也正是没被标准化的部分。
- 连接:每遇到一个新加载的模块,为其创建作用域并将模块内声明的所有绑定填充到该作用域中,其中包括由其它模块导入的内容。
- 运行时:最终,在每一个新加载的模块体内执行所有语句。此时,导入的过程就已经结束了,所以当执行到达有一行 import 声明的代码的时候……什么都没发生!
ES6 系统实现为:在编译时计算所有依赖并将所有模块打包成一个文件,通过网络一次传输所有模块!像 webpack 这样的工具就实现了这个功能。
静态 vs 动态:论规则及破例之法
- JavaScript 作为一门动态语言已经得到了一个令人惊讶的静态模块系统,在模块的最外层作用域使用 import 和 export,不可在条件语句中使用,也不能在函数作用域中使用 import。
- 所有导出的标识符一定要在源代码中明确地导出它们的名称。
- 一个模块的所有依赖必须在模块代码运行前完全加载、解析并且及早连接,不存在一种通过 import 来按需懒加载的语法
- import 模块产生的错误没有错误恢复机制。一旦有一个模块无法加载或连接,所有的模块都不会运行,而且你不能在 try/catch 代码块中捕捉 import 的错误信息。webpack在编译时会检查这些错误。
- 不支持在模块加载依赖前运行其它代码的钩子,这也意味着无法控制模块的依赖加载过程。
- ES6 模块语法非常静态
本文参考《ES6-In-Depth》