模块化
CommonJs
导出
- exports
- module.exports
- 但是为了实现模块的导出,Node中使用的是Module的类,每一个模块都是Module的一个实例,也就是 module
- 所以在Node中真正用于导出的其实根本不是exports,而是module.exports
- 本质上,这两个导出是一样的
引入——require(X)
- X是一个核心模块
- 直接返回核心模块,并且停止查找
- X是以 ./ 或 ../ 或 /(根目录)开头的,将X当做一个文件在对应的目录下查找
- 如果有后缀名,按照后缀名的格式查找对应的文件
- 如果没有后缀名,会按照如下顺序
- 直接查找文件X
- 查找X.js文件
- 查找X.json文件
- 查找X.node文件
- 没有找到对应的文件,将X作为一个目录
- 查找目录下面的index文件
- 查找X/index.js文件
- 查找X/index.json文件
- 查找X/index.node文件
- 如果没有找到,那么报错:not found
- 直接是一个X(没有路径),并且X不是一个核心模块
模块加载过程
- 模块在被第一次引入时,模块中的js代码会被运行一次
- 模块被多次引入时,会缓存,最终只加载(运行)一次
- 每个模块对象module都有一个属性:loaded
- 为false表示还没有加载,为true表示已经加载
- 如果有循环引入,Node采用的是深度优先算法加载模块
CommonJs加载过程
- 加载js文件的过程是运行时加载的,并且是同步的
- CommonJS通过module.exports导出的是一个对象,由于指着同一对象,所以一个地方修改了对象属性,其他地方也会被修改
ES Modules
- 使用了import和export关键字
- 它采用编译期的静态分析,并且也加入了动态引用的方式
导出——export
- 在语句声明的前面直接加上export关键字
- 将所有需要导出的标识符,放到export后面的
{}
中- 这里的
{}
里面不是ES6的对象字面量的增强写法,{}也不是表示一个对象的
- 这里的
- 导出时给标识符起一个别名
- 默认导出——default
导入——import
import {标识符列表} from '模块'
- 这里的{}也不是一个对象,里面只是存放导入的标识符列表内容
- 导入时给标识符起别名
- 通过
*
将模块功能放到一个模块功能对象上
通过import加载一个模块,是不可以在其放到逻辑代码中的,因为ES Module在被JS引擎解析时,就必须知道它的依赖关系。
假如要动态引入,可以使用import()函数,来动态加载
export 与 import 结合
export {xxx as yyy} from './zzz'
ES Module加载过程
- 加载js文件的过程是编译(解析)时加载的,并且是异步的
- 所以有时候也称ES Module是静态解析的,而不是动态或者运行时解析的
- export导出的是变量本身的引用,在导出一个变量时,js引擎会解析这个语法,并且创建模块环境记录,模块环境记录会和变量进行绑定,并且这个绑定是实时的,
- 在导入的地方,我们是可以实时的获取到绑定的最新值的,且在导入的地方不可以修改变量,因为它只是被绑定到了这个变量上(其实是一个常量)