node 模块引入与加载机制

本文详细介绍了Node.js中模块的概念,包括模块的加载机制、核心模块与文件模块的区别,以及如何通过require方法加载模块。同时,文章还讲解了模块对象module、exports的使用,以及自定义require方法的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、说在前面

在Node.js中,以模块为单位划分功能,通过一个完整的模块加载机制使得开发人员可以将应用程序划分为多个不同的部分。模块的使用可以提高代码重用率,提高应用程序的开发效率,而且开发人员可以根据具体的需求引入第三方模块或者自定义模块到应用程序中。

二、模块引入与常用内置对象

1. module

nodejs认为一个js文件就是一个模块,每个模块都有一个全局对象module,同时module对象中有一个对象exports,这个对象被加载一次之后会被缓存,里面提供了模块的父子模块关联信息,即父模块被哪些模块引用,子模块引用了哪些模块。

  Module {
         id: '.',
         exports: {},
         parent: null,
         filename: 'E:\\workspace\\myRequre.js',
         loaded: false,
         children: [],
         paths: [ 'E:\\workspace\\ node_modules', 'E:\\workspace\\node_modules', 'E:\\node_modules' ] 
}

2. exports

exportsmodule.exports对象的别名,提供便捷的属性和方法设置。

3. require

require可以加载文件模块(.js、.json、.node)和nodejs核心模块,最终获取到的是module.exports对象。第一次加载的时候根据路径或模块名称进行查找,第二次从缓存中获取到module.exports对象,如果没有发现指定模块就会抛出异常。

手动模拟require方法

在下述代码中,source变量即为path路径对应文件的二进制内容。该内容与字符串进行拼接后成为string类型。该文件中的内容有同名exports变量,又因为package的字符串对应的匿名函数的参数也有exports变量,故相当于为参数exports添加属性,故当return module.exports时,将参数module中的exports对象返回,即改变后的exports对象。

function MyRequre(path){
 
	function Module(){
		this.exports = {};
	}
 
	var fs = require('fs');
 
	var source = fs.readFileSync(path,'utf8');
 
	var package = "(function(exports,module){"+source+" return module.exports;})";
 
	var callback = eval(package);//将字符串转成函数
 
	var module = new Module();
	var fn  = callback(module.exports, module);
 
	return fn;
}
//foo.js
function a(){
    console.log("1111");
}
   
 exports.a =a;
//使用
var fn = MyRequre('./foo.js');
console.log(fn.a());//1111

三、模块加载机制

1. package

包是将一堆的文件联系起来的一种机制,nodejs就是在模块的基础之上进一步组织js代码。

在这里插入图片描述

规范的包目录结构
文件/目录说明
package.json包描述文件。不仅开发者阅读使用,nodejs也使用(查找文件时…)
bin存放可执行的文件目录
lib存放js的目录
doc存放文档的目录
test单元测试用例代码
package.json文件
1. 文件大致模板
{
  "name": "test",
  "version": "0.0.1",
  "description": "",
  "main": "index.js",
  "keywords": [
  ],
  "author": "KingNigel",
  "repository": {
    "type": "git",
    "url": "https://github.com/KingNigel/test.git"
  },
  "bugs": {
    "url": "https://github.com/KingNigel/test/issues"
  },
  "license": "MIT",
  "devDependencies": {
    "express": "4.*"
  },
  "dependencies": {
    "express": "4.*"
  }
}
2. 文件属性描述
属性描述
name包名
description包描述
version包版本号
keywords关键词组,在npm中分类搜索使用
author包作者
main配置包入口,默认是包的根目录下的index.js
dependencies包依赖项,npm会自动加载依赖包
scripts指定运行脚本命令npm命令行

2. npm

npm install:本地安装
  • 将安装包下载到./node_modules下(运行npm命令时所在的目录),如果没有node_modules目录,会在当前执行npm的命令的目录下生成node_modules目录
  • eg:如果当前目录是c:\123>npm install ***,则该模块会被安装到c:\123\node_modules\***
  • 可以通过require()来引入安装的包
  • 该模块只能在本工程下使用
npm install -g:全局安装
  • 模块将被下载安装到全局目录中,该模块可在全局使用
  • 可通过npm config set prefix "目录路径"来设置。
  • eg: 当使用了npm install -g express安装了express框架后, 我们就可以在电脑里的某一个文件夹下,打开控制台,直接使用express mvc创建项目,否则会遇到"express’不是内部或外部命令,也不是可运行的程序”错误

在这里插入图片描述

四、node模块

node模块分为核心模块和文件模块

模块文件

  1. 后缀名为.jsjavascript脚本文件
  2. 后缀名为.jsonjson文本文件
  3. 后缀名为.node的经过编译的二进制模块文件。

1. 核心模块

  1. 核心模块的源码都在lib子目录中,为了提高运行速度,他们安装的时候,都会被编译成二进制文件(后缀名为.node)。
  2. 在引入核心模式时直接使用require("模块名称")即可,不需要写路径。
  3. 在引入核心模块时,如果写错模块名则require方法会抛出异常。
  4. 核心模块具有最高的加载优先权,如果有模块与其名称冲突,Node.js总是加载核心模块

在这里插入图片描述

2. 文件模块

  1. 文件模块如果不加上扩展名,则在查找时会按照.js、.json、.node的顺序为其加上扩展名
  2. 文件模块有两种加载形式:① 按路径加载 ② 在node_modules文件中查找加载
  3. 文件模块按路径加载又分为 ① 按照绝对路径加载 ② 按照相对路径加载

模块加载的顺序

在加载模块时先去缓存中查找,如果为查找到再进行以下情况判断。

1. 模块有路径但没有扩展名:

按照其path先定位到对应的路径下,根据.js、.json、.node的顺序为path中的文件名加上后缀进行文件查找,如果找到则返回,如果未找到,则会将path中的文件名视为目录名进行查找,如果找到同名目录,则定位到该目录下,先去查找该目录下的package.json文件,通过JSON.parse()解析出包描述对象,再取出main属性指定的文件名进行定位。如果文件缺少扩展名,将会进入扩展名分析的步骤。如果该目录下没有package.json或者main属性指定的文件名错误,则会将index当做默认的文件名,然后依次查找index.js、index.json、index.node,如果找到则返回,如果未找到则require方法会抛出异常。

2. 模块有路径且有扩展名

按照其path定位到对应的路径下,根据文件名查找对应的文件,如果找到则返回,如果未找到,require方法会抛出异常。

3. 模块没有路径且没有扩展名

① 先去查找核心模块,如果不是核心模块则转到步骤②,否则返回核心模块。
② 去当前目录下的node_modules按照.js、.json、.node的顺序为模块名称加上扩展名然后去查找,如果找到则返回文件,否则将模块名称视为目录名称,在node_modules/目录名下查找package.json文件,通过JSON.parse()解析出包描述对象,再取出main属性指定的文件名进行定位。如果文件缺少扩展名,将会进入扩展名分析的步骤。如果该目录下没有package.json或者main属性指定的文件名错误,则会将index当做默认的文件名,然后依次查找index.js、index.json、index.node,如果找到则返回,如果未找到则执行步骤③
③ 去父目录中的node_modules中继续按照步骤②查找,直到根目录。如果到根目录还未找到文件,则执行步骤④。
在全局目录查找:如果在操作系统的环境变量中设置NODE_PATH变量,并且已经将变量值设置为一个有效的磁盘目录,在使用require()方法加载模块时只指定了模块的名称而没有指定模块的路径,而且Node.js从其他路径中找不到需要被加载的模块文件时,Node.js将会从NODE_PATH变量值所指向的磁盘目录中寻找并加载模块文件。如果最终都没有找到,require()方法就会抛出异常。

4. 模块没有路径且有扩展名

① 去当前目录的node_modules下查找该文件名,如果有则返回,如果没有则去父目录中的node_modules下查找,直至到根目录,如果仍旧未找到,则会去全局目录下查找,依旧未找到则require方法会抛出异常。

require方法的查找策略

在这里插入图片描述

参考文档:https://www.jianshu.com/p/f740287a5df7
https://blog.youkuaiyun.com/u013174239/article/details/79705542
https://blog.youkuaiyun.com/WuLex/article/details/82225210

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值