模块化

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引擎会解析这个语法,并且创建模块环境记录,模块环境记录会和变量进行绑定,并且这个绑定是实时的,
  • 在导入的地方,我们是可以实时的获取到绑定的最新值的,且在导入的地方不可以修改变量,因为它只是被绑定到了这个变量上(其实是一个常量