今天看啥  ›  专栏  ›  一筐芒果

[Node系列] 模块机制一

一筐芒果  · 掘金  ·  · 2021-05-19 11:53
阅读 11

[Node系列] 模块机制一

1. CommandJS的出发点

1.1. CommandJS的模块规范

CommandJS的模块规范分为三个部分: 模块引用模块定义模块标识.

  1. 模块引用

模块引用的方式:

const fs = require('fs')// Node核心模块

// 用户定义文件模块
const add = require('./add.js') // 这里的.js可以省略(后续会说到
复制代码
  1. 模块的定义/模块的导出 导出文件通过exports对象导出当前模块中的方法或者变量.
//math.js 
exports.add = function(){}

// add.js
exports.add = function(){}
复制代码
  1. 模块标识 模块标识就是传递给require()方法的参数, 这里规定需要是小驼峰命名(例子:setName),或者以.或者..开头的相对路径、或者绝对路径.

2. Node的模块实现

Node通过exports、require、module来进行模块的定义,模块的引用,模块的导出.但是内部是怎么实现的呢, 下面将介绍Node实现这这些所做的处理.
复制代码

1.路径分析 2.文件定位 3.编译执行

  • Node中, 模块分为两类: 核心模块文件模块
  • 核心模块再NOde源代码的编译过程中, 编译进了二进制文件, 在Node进程启动时,核心部分就被直接加载进内存中, 所以, 这部分核心模块引入的时候, 文件定位和编译执行两个步骤被省略, 所以, 加载速度最快.
  • 文件模块是在运行时动态加载, 需要完整的路径分析、文件定位、编译执行的过程.

2.1 优先从缓存加载

和浏览器会缓存js文件从而减少数据资源的获取一样, Node的模块引入, 也会进行数据资源的缓存, 第一次require进环境的文件, 会被缓存进`Module.caches[]`中
缓存加载是不管核心模块还是文件模块,都会优先执行的. 
复制代码

2.2 路径分析和文件定位

前面提到的require()中的参数就是模块的标识符, 对于引入的标识符不同的情况,模块的查找和定位也是不同的.
复制代码

1. 模块标识符分析

  • 核心模块: 如fs、path等

  • .或..开始的相对路径文件

  • 非路径形式的文件模块, 如自己定义add.js模块

  • 核心模块:

核心模块的优先级仅次于缓存加载, 因为其已经被编译成了二进制代码,加载速度很快 注意: 这里如果想编写一个和核心模块重名的模块是不行的, 加载不会成功.

  • 路径形式模块

以.,..和/开始的标识符, 都会被当作文件模块来处理,在分析路径模块时, require()会将路径转成真实路径,找到文件, 编译执行后, 放进缓存, 让第二次加载更快. 这里需要查找文件位置, 所以,查找过程中可以节约大量时间,加载速度慢于核心模块.

  • 自定义模块

自定义模块不是核心也不是文件模块, 可能是一个特殊的文件模块, 一个文件或者包的形式,这是最费事的.

  • 怎么查看当前文件的module路径, 可以使用 输出module.paths.
[
'/Users/cookie/Desktop/Leetcode/Node/node_modules',
'/Users/cookie/Desktop/Leetcode/node_modules',
'/Users/cookie/Desktop/node_modules',
'/Users/cookie/node_modules',
'/Users/node_modules',
'/node_modules'
]
复制代码

这里路径的规则是 当前文件下的node_modules目录 父目录下的node_modules 爷爷目录下的node_modules ... 这里和原型链一样,从内向外查找, 直到查找到为止

2. 文件定位

从缓存中加载的优化策略,可以使得第二次引入的时候不需要进行路径分析, 文件定位和编译执行过程, 大大提高了再次加载模块时的效率.

  • 文件拓展名分析

require()在分析标识符的过程中,如果没有声明后缀名,将使用.js=》.json=》.node的顺序去查找. 这里有个小技巧: 在引入 .json, .node文件时候, 最好写上后缀, 这样会加快这两类文件的加载速度.

2.3模块编译

文件的编译和执行是引入模块的最后一个阶段, Node针对不同的文件, 载入的方式和处理方式也是不一样的.
复制代码
  • .js文件: 通过fs模块读取文件之后编译.
  • .json文件:通过fs模块读取文件后, 使用JSON.parse()处理后导出.
  • .node文件:这是用C/C++编写的拓展文件,通过dlopen()加载最后编译生产的文件.

致谢

参考文献《深入浅出Node.js》朴灵 此文章为膜拜大佬的著作后的记录, 不是小菜鸡的自己研究, 仅此作为记录, 督促自己好好学习, 感谢!!! 🙏




原文地址:访问原文地址
快照地址: 访问文章快照