NodeJS复习(模块化)

118 阅读5分钟

最近工作中不涉及到nodejs的使用,之前学习的相关知识基本快忘记了,利用周末的时间复习一下。

 一、模块化 

早起javaScript不支持模块化,使用起来经常需要借助闭包等方式来分离作用域,社区诞生了amd、cmd、commonJS等规范,js自己也在es6中新增了模块化的方式。

1、commonJS

  • commonJS规范里通过exports来导出模块,在nodeJS里面,真正实现模块导出的是module.exports对象,node内部会把exports对象赋值给exports,本质上是对象的传址;
  • CommonJS中是没有module.exports的概念的;
  •  但是为了实现模块的导出,Node中使用的是Module的类,每一个模块都是Module的一个实例,也就是 module;
  • 所以在Node中真正用于导出的其实根本不是exports,而是module.exports; 
  • 因为module才是导出的真正实现者;

cmmonJS通过require导入

  1. require的查找规则
  • 情况一:X是一个核心模块,比如path、http p 直接返回核心模块,并且停止查找

  • 情况二:X是以 ./ 或 ../ 或 /(根目录)开头的

  • 第一步:将X当做一个文件在对应的目录下查找;
    1.如果有后缀名,按照后缀名的格式查找对应的文件 p 2.如果没有后缀名,会按照如下顺序:

1> 直接查找文件X ü 2> 查找X.js文件  
3> 查找X.json文件 ü 4> 查找X.node文件

第二步:没有找到对应的文件,将X作为一个目录 p 查找目录下面的index文件

1> 查找X/index.js文件  
2> 查找X/index.json文件 ü 3> 查找X/index.node文件

情况三:直接是一个X(没有路径),并且X不是一个核心模块,会在上层目录的node\_modules中查找,

 /Users/coderwhy/Desktop/Node/TestCode/04\_learn\_node/0 5\_javascript-module/02\_commonjs/main.js中编写 require('why’)

如果没有找到,那么报错:not found 

2、模块的加载规则

  1. 结论一:模块在被第一次引入时,模块中的js代码会被运行一次

  2. 结论二:模块被多次引入时,会缓存,最终只加载(运行)一次

  3. 结论三:如果有循环引入,那么加载顺序是什么?

  4. p 这个其实是一种数据结构:图结构;

    p 图结构在遍历的过程中,有深度优先搜索(DFS, depth first search)和广度优先搜索(BFS, breadth first search);

    p Node采用的是深度优先算法:main -> aaa -> ccc -> ddd -> eee ->bbb 

三、 CommonJS规范缺点

1、CommonJS加载模块是同步的:
同步的意味着只有等到对应的模块加载完毕,当前模块中的内容才能被运行;
这个在服务器不会有什么问题,因为服务器加载的js文件都是本地文件,加载速度非常快;

2、如果将它应用于浏览器呢?
浏览器加载js文件需要先从服务器将文件下载下来,之后在加载运行;
那么采用同步的就意味着后续的js代码都无法正常运行,即使是一些简单的DOM操作;

3、所以在浏览器中,我们通常不使用CommonJS规范:
当然在webpack中使用CommonJS是另外一回事;
因为它会将我们的代码转成浏览器可以直接执行的代码;

4、在早期为了可以在浏览器中使用模块化,通常会采用AMD或CMD:
但是目前一方面现代的浏览器已经支持ES Modules,另一方面借助于webpack等工具可以实现对CommonJS或者ES Module代码的转换;

AMD和CMD已经使用非常少了 

二、ES Module

1、ES Module和CommonJS的模块化有一些不同之处:

  • 一方面它使用了import和export关键字(不是对象);
  • 另一方面它采用编译期的静态分析,并且也加入了动态引用的方式; 

2、采用ES Module将自动采用严格模式:use strict

3、ES Module加载js文件的过程是编译(解析)时加载的,并且是异步的:

  •  编译时(解析)时加载,意味着import不能和运行时相关的内容放在一起使用: p 比如from后面的路径需要动态获取;
  • 比如不能将import放到if等语句的代码块中;
  •  所以我们有时候也称ES Module是静态解析的,而不是动态或者运行时解析的;

4、异步的意味着:JS引擎在遇到import时会去获取这个js文件,但是这个获取的过程是异步的,并不会阻塞主线程继 续执行;

  • 也就是说设置了 type=module 的代码,相当于在script标签上也加上了 async 属性;
  • 如果我们后面有普通的script标签以及对应的代码,那么ES Module对应的js文件和代码不会阻塞它们的执行 

5、ES Module通过export导出的是变量本身的引用:

  • export在导出一个变量时,js引擎会解析这个语法,并且创建模块环境记录(module environmentrecord);
  • 模块环境记录会和变量进行 绑定(binding),并且这个绑定是实时的; p 而在导入的地方,我们是可以实时的获取到绑定的最新值的;

6、所以,如果在导出的模块中修改了变化,那么导入的地方可以实时获取最新的变量;
7、注意:在导入的地方不可以修改变量(对象可以改),因为它只是被绑定到了这个变量上(其实是一个常量) 

三、 Node对ES Module的支持

在最新的Current版本中,支持es module我们需要进行如下操作:

  • 方式一:在package.json中配置 type: module(后续学习,我们现在还没有讲到package.json文件的作用) p 方式二:文件以 .mjs 结尾,表示使用的是ES Module;
  • 这里我们暂时选择以 .mjs 结尾的方式来演练: