Node.js:模块化
模块化
遵守固定的规则,将程序划分成一个个小的结构,一个结构叫做模块。在各个结构中编写属于自己的逻辑代码,有自己的作用域,不会影响到其他结构。每个结构内可以将希望暴露的变量、函数、对象等导出给其他结构使用。
将程序划分成一个个结构的过程就是模块化的过程。
模块分类
内置模块
所有内置模块在安装 node.js 的时候就已经编译成二进制文件,可以直接加载运行部分内置模块,在 node.exe 这个程序启动的时候就已经默认加载了,所以可以直接使用,之前我们使用的就是内置模块。
第三方模块
第三方门模块需要安装后才能使用
用户自定义模块
用户自定义模块就是开发者在开发过程中自己编写的js代码文件
module对象
每一个自定义模块都有一个module对象,包含当前模块的基本信息
exports与module.exports指向同一个对象,每个模块导出的都是module.exports。
exports是CommonJS规范规定必须有的。
通过控制台我们可以看到,module对象上挂载的属性,其中的exports就是用来导出的对象,导出的内容挂载在exports对象中。
require
require是一个函数,用于引入一个文件中导出的对象.
模块查找规则
调用require函数时,判断参数是否是以./或者…/开头,如果不是则认为是模块名:先从核心模块中查找如果找到直接返回核心模块,并且停止查找。
如果核心模块中没有找到就作为第三方模块加载。先尝试从/node_modules文件夹中加载第三方模块,如果没有找到对应的第三方模块,则移动到上一层目录中进行加载,直到文件系统的根目录。
如果是则认为是加载自定义模块,./从当前目录下开始查找, …/从上级目录开始查找, / 从计算机根目录开始查找。如果文件有后缀名,根据后缀名去路径查找对应的文件。
如果省略了文件扩展名,ndoejs 会按顺序分别尝试加载以下的文件:
- 按照确切的文件名进行加载
- 补全.js扩展名进行加载
- 补全.json扩展名进行加载
- 补全.node扩展名进行加载
- 加载失败终端报错
如果不是一个文件,则作为一个目录加载:
在被加载的目录下查找一个叫做package.json的文件,并寻找main属性,作为reuqire()加载的入口
如果目录里没有package.json文件,或者main入口不存在或无法解析,则node将会尝试加载目录下的index.js文件:
- 查找index.js文件
- 查找index.json文件
- 查找index.node文件
如果以上两步都失败了,则node.js会在终端打印错误消息,报告模块的缺失:not found
模块加载过程
require某个文件时,会执行该文件里的代码,并设置module.loaded为true,当module.loaded为true时表示当前魔块已被加载。多次require(),被引用的模块的代码并不会被执行多次,只在第一次reuqire时执行一次。
内置模块的加载优先级最高。
如果有循环引用,加载顺序是一种图结构,Node使用深度优先算法:main -> aaa -> bbb -> ccc -> ddd -> eee -> bbb
ESModule
import:引入模块
// 给导入的变量起别名
import {fName as name } from 'xxx';
import * as xxx from 'xxx';
export:导出模块
export { // 不是对象,在{}中放置需要导出的参数列表
name as fName // 给导出的变量起别名
}
export {name as fName } from 'xxx';
export default { // 默认导出,只能导出一个
}
ES Module 加载过程
ES Module 加载js文件的过程是编译时加载的并且是异步的:
编译时,import不能和运行时相关的内容放在一起使用
js引擎在遇到import时回去获取这个文件但是这个过程是异步的,并不会阻塞主线程继续执行
ES Module通过export 导出的是变量本身,导出的变量是常量,不能被赋值,如果导出的是对象可以对对象的属性进行修改
CommonJS和ES Module交互
通常情况下,CommonJS不能加载ES module
CommonJS同步加载,ES Module需要对文件进行静态分析,当代码执行的时候文件可能没有下载完毕
多数情况下,ES Module可以加载CommonJS
ES Module 加载CommonJS的时候一般都是在代码运行的时候此时CommonJS导出的内容基本上是确定了的