0

0

关于Node模块机制的解析

不言

不言

发布时间:2018-07-11 15:18:18

|

1763人浏览过

|

来源于php中文网

原创

这篇文章主要介绍了关于node模块机制的解析,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下

模块机制

一、CommonJS出现背景

js实现编写后端程序的不足之处

1. 没有模块系统(js一个先天不足就是模块功能)

2. ECMAScript仅仅定义了js的核心库,但是对于文件系统、IO系统等却没有标准的API。HTML5虽然在一直致力于推进标准化,但是这些标准耶都是前端的。

3. 没有标准接口,没有定义过服务器或者数据库的接口。

4. 缺乏包管理系统,没有自动安装和管理依赖的能力。

而CommonJS的出现正好弥补了没有标准的这一个缺点。

CommonJS 规范了 模块二进制Buffer二进制I/0,进程环境文件系统web服务器网管接口包管理等

二、CommonJS和Node的关系

Node的出现离不开CommonJS规范的影响,而CommonJS能以一种独寻常的姿态出现在各大公司的代码中,离不开Node优异的表现。

1894707805-5b4055ccaccd9_articlex[1].png

三、CommonJS的模块规范

1. 模块引入

var http = require("http");

2. 模块导出

// math.js
exports.add = function(){
    ...
}
// program.js
Var math = require('math.js');

exports.increament = function(val){
    return math.add(val);
}

3. 模块标识

模块标识就是require()的括号中的参数,必须是小驼峰结构,可以是相对路径也可以是绝对路径,还可以没有后缀名。


四、Node的模块实现

node 的模块实现实际上是借鉴了CommonJS的部分,并不是全部照搬,而且也增加了一些自己需要的东西进去。

nodejs-require[1].jpg

1. 模块引入

node中模块引入需要经过:

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

2. 模块分类

1. 核心模块 Node提供的
2. 文件模块 用户编写的

3. 核心模块

核心模块部分在Node源码加载中就编译完毕了,编译成了二进制执行文件,在Node进程启动后,部分核心模块就直接加载进内存中,所以可以不用`文件定位`和`编译`,因此部分核心模块的加载时最快的。

4. 文件模块

文件模块则是在运行时动态加载,要经过完整的路径分析、文件定位、编译执行,一次一般比较慢。

5. 缓冲加载

node对二次加载的核心模块,一律采用的时缓冲优先的原则。

6. 路径分析和文件定位

模块标识符分析
  • 核心模块

核心模块的加载仅次于缓冲加载
  • 路径形式的文件模块

首先将其转换为真实的路径,然后以真实路径作为索引,将其编译后放进缓冲中,等待调用。
由于是通过确切的文件地址找到的,所以需要花费一定的时间,
  • 自定义模块

这是非核心模块,也不是路径形式的标识,它是一种特殊的文件模块,可能是一个文件或者是一个包。这种查找是最费时间的。首先需要知道一下/模块路径/的概念/

> 例子

比如你要加载一个包,这个包放在了node_modules文件夹下,你要引入的话可以不以路径的形式写,可以是只写名称。(也就是引入一个自己npm的包)

console.log(module.paths);
// 会得到下面的数组
[ 'D:\myweb\node\node\module\node_modules',
  'D:\myweb\node\node\node_modules',
  'D:\myweb\node\node_modules',
  'D:\myweb\node_modules',
  'D:\node_modules' ]

这就是模块路径了,查找机制是: 首先在当前路径下找是否有node_modules文件夹下的该包,如果没有就查找上一层目录,依次类推,直到根目录下的node_modules,如果依旧没有找到,那么就报错了。

// 显然这种情况就是导致它速度较慢的主要原因了,(查找的路径越深就越慢),但是我们可以通过一些小技巧来经可能的减少这种情况哦~~
文件定位

文件定位中还需要注意的包括有文件扩展名目录包的处理

node 的模块引入的时候是可以不写扩展名的,node会按照js json node的顺序来分析。依次尝试。由于尝试使用的是node中fs模块的同步文件查找,因此可能会导致阻塞情况发生,因此这里我们需要注意两个小技巧了:

小技巧:

1. json node文件最好加扩展名

2. 同步配合缓冲可以环节Node单线程阻塞调用的缺陷

靠岸学术
靠岸学术

一款集翻译,阅读,文献管理于一体的英文文献阅读器

下载

另外,如果没有找到对应的文件,确实找到了一个目录,那么将会将其当作是一个包来处理了。

如何在这个包下找到我们需要引入的入口文件对呢?

1. 首先找是否含有package.json,如果有,则分析它的main属性,找到main属性对应的那个文件。

2. 如果没有package.json或者是main解析失败了,那么就找文件名为index的文件,依次从index.js index.json index.node查找。

3. 如果在该目录下依旧没有找到,那么就查找写一个匹配的目录,如果仍然没有找到,那么就报错了。


6. 模块编译

node中每一个模块都是一个对象,当定位到一个文件的时候,node就会将其包装成一个module对象,然后根据不同的文件名,其载入方法也不同的。

  1. js文件: 通过fs的同步读取文件来执行

  2. json文件,通过fs同步读取文件,用Json.parse()来得到对象,将其赋值给Module.exports

  3. node文件,这事c/c++编写的扩展文件,通过Process.dlopen()方法来加载执行。(不需要编译)

2307702596-5b408e8838bfc_articlex[1].png


7. module.exports和exports的区别和联系

exports = module.exports = {};的区别就和 var a = {}; b = a;的区别一样.

首先要明确的一点,module是一个对象 {Object}。
当你新建一个文件,比如mo.js,文件内容如下:

console.log(module);

然后在CMD里执行这个文件node mo.js,就能看到module其实是一个Module实例,你可以这么理解,NodeJS中定义了一个Module类,这个类中有很多属性和方法,exports是其中的一个属性:

function Module {
  id : 'blabla',
  exports : {},
  blabla...
}

当每一个文件被执行或者时require的时候,node就会创建一个module实例。var module = new Module();

console.log(module); //你会看到Module中的exports为空对象{}
module.exports = {
  print : function(){console.log(12345)}
}
console.log(module); //你会看到Module中的exports对象已经有了print()方法

module.exports 其实就是module实例中的module添加方法或者属性。

console.log(module); //你会看到Module中的exports为空对象{}
console.log(exports); //你会看到Module中的exports为空对象{}
module.exports = {
  print : function(){console.log(12345)}
}
console.log(module); //你会看到Module中的exports对象有了print()方法
exports.name = '小白妹妹';
console.log(module); //你会看到Module中的exports对象不仅有了print()方法,还有了name属性

不难看出exports其实就是module.exports的一个引用。

// 你可以这么理解
var module = new Module();
var exports = module.exports;

当你require的时候,返回的就是module.exports的内容。

// 常用场景分析
module.exports.name = '小白妹妹';
exports.age = 10;
module.exports.print = function(){console.log(12345)};
//如果只是添加属性和方法,两者可以混用。

// 也可以
module.exports = {
  name = '小白妹妹';
};
exports.age = 10;
module.exports.print = function(){console.log(12345)};

// 【X】但是不可以
module.exports = {
  name = '小白妹妹';
};
exports = {age:10}; // exports现在是{age:10}这个对象的引用,不再是module.exports的引用了
console.log(module); //你会看到Module的exports中只有name属性!!!

// 【X】
exports.age = 10; 
console.log(module); //你会看到Module的exports中多了age属性
module.exports = {
   name = '小白妹妹';
};
// 直接改变了module.exports的引用,之前的属性值也被覆盖掉了。
console.log(module); //你会看到Module的exports中还是只有name属性!!!

总结

  1. 改变exports的指向后所添加的exports.xxx都是无效的。因为require返回的只会是module.exports

  2. 不能在使用了exports.xxx之后,改变module.exports的指向。因为exports.xxx添加的属性和方法并不存在于module.exports所指向的新对象中。

  3. 对于要导出的属性,可以简单直接挂到exports对象上

  4. 对于类,为了直接使导出的内容作为类的构造器可以让调用者使用new操作符创建实例对象,应该把构造函数挂到module.exports对象上,不要和导出属性值混在一起

Node2[1].png

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

关于Node基本概念的介绍

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

46

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

178

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

51

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

92

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

102

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

227

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

532

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

171

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Node.js 教程
Node.js 教程

共57课时 | 13.3万人学习

【web前端】Node.js快速入门
【web前端】Node.js快速入门

共16课时 | 2.1万人学习

Node.js-前端工程化必学
Node.js-前端工程化必学

共19课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号