> 文章列表 > Node【模块系统】

Node【模块系统】

Node【模块系统】

文章目录

  • 🌟前言
  • 🌟Nodejs模块系统
  • 🌟为什么需要模块化
  • 🌟什么是Nodejs模块
  • 🌟Nodejs模块分类
    • 🌟文件模块的分类
    • 🌟调用内置模块
    • 🌟调用文件模块
  • 🌟Nodejs模块使用
    • 🌟加载模块
    • 🌟exports 导出模块
    • 🌟module.exports 导出模块
    • 🌟module的其它API
  • 🌟Nodejs模块其他
    • 🌟模块初始化
    • 🌟主模块
    • 🌟变量
      • 🌟__filename
      • 🌟__dirname
    • 🌟node_modules目录
  • 🌟写在最后


在这里插入图片描述

🌟前言

哈喽小伙伴们,新的专栏 Node 已开启;这个专栏里边会收录一些Node的基础知识和项目实战,今天带领大家初识一下 Node模块系统让我们一起来看看吧🤘

🌟Nodejs模块系统

模块化做为一种现代化的设计方法,这个概念最早起源于生产制造行业。如今这个概念已经被各行各业来衍生应用,在软件开发中也大量的采用了模块化思想。

所谓的模块化思想,将一个大程序按照功能划分为若干个小的模块,每个小程序模块完成一个特定的功能,所有的模块按某种方法组装起来,成为一个整体,完成整个系统所要求功能的程序设计方法。

🌟为什么需要模块化

模块化可以使你的代码低耦合,功能模块直接不相互影响。

在计算机程序的开发过程中,随着程序复杂度上升代码越写越多,在一个文件里代码就会越来越长,不易维护。为了编写可维护的代码,我们把完成特定功能的代码分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。

JavaScript 做为一门为网页添加交互功能的简单脚本语言问世,在诞生时并不包含模块系统,随着 JavaScript 解决问题越来越复杂,脚本文件增多,并且有复杂的依赖关系的时候就很容易出现一些变量的属性或方法被覆盖或改写,导致变量污染。这是因为js没有命名空间,不像其他语言通过命名空间可以有效的避免重名问题。

为了解决命名冲突与文件依赖问题,Javascript模块化编程,已经成为一个迫切的需求。因此Nodejs就引入了模块化这个思想。理想情况下,将函数和方法包含在不同的模块中,就不会相互污染,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。

模块化思想解决问题:

  1. 可维护性:每个模块都是单独定义的,之间相互独立。模块尽可能的需要和外部撇清关系,方便我们独立的对其进行维护与改造。维护一个模块比在全局中修改逻辑判断要好的多。
  2. 命名冲突:为了避免在JavaScript中的全局污染,我们通过模块化的方式利用函数作用域来构建命名空间,避免命名冲突。
  3. 文件依赖:一个功能可能依赖一个或多个其他文件,使用是除了引入它本身还需要考虑依赖文件,通过模块化 我们只需要引入文件,无需考虑文件依赖(模块化可以帮助我们解决文件依赖问题)。
  4. 可复用性:虽然粘贴复制很简单,但是要考虑到我们之后的维护以及迭代。

🌟什么是Nodejs模块

为了让Nodejs的文件可以相互调用,Nodejs基于CommonJS规范提供了一个简单的模块系统。而模块化是面对复杂的业务场景不可或缺的工具。

模块是Node.js应用程序的基本组成部分。一个 Node.js文件就是一个模块,这个文件可能是JavaScriptJSON或者编译过的C/C++ 扩展的代码

CommonJS是一个旨在定义一系列规范的项目,以帮助开发服务器端JavaScript应用程序。CommonJS团队试图解决的一个领域就是模块。

CommonJS 的规范中,每个 JavaScript 文件就是一个独立的模块上下文(module context)(模块作用域),在这个上下文中默认创建的属性都是私有的。也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的。

也就是说在当前模块下定义的所有的变量/函数,都是属于这个模块的,作用范围只是当前模块。

🌟Nodejs模块分类

在Nodejs中常见的模块主要有两种类型,即:内置(原生)模块 和 文件模块。

模块类型 描述
内置模块/原生模块 即Node.js API提供的内置模块(也叫核心模块),原生模块在启动时已经被加载。(如:os模块、http模块、fs模块、buffer模块、path模块等)
文件模块/第三方模块 为动态加载模块,加载文件模块的主要由原生模块module来实现和完成。原生模块在启动时已经被加载,而文件模块则需要通过调用Node.js的require方法来实现加载。

🌟文件模块的分类

在 文件模块/第三方模块 中,又分为3类模块。这三类文件模块以后缀来区分,Node.js会根据后缀名来决定加载方法。

类型 描述
.js Javascript模块,通过fs模块同步读取js文件并编译执行。
.node C/C++ 模块,通过C/C++进行编写的Addon。通过dlopen方法进行加载。
.json json 模块,读取文件,调用JSON.parse解析加载。

🌟调用内置模块

//调用内置模块不需要指定路径
var http = require('http');

🌟调用文件模块

//调用文件模块必须指定路径,否则会报错
var sum = require('./sum.js');

🌟Nodejs模块使用

Nodejs 遵循 CommonJS规范,该规范的核心思想是允许模块通过 require 函数来同步加载所要依赖的其他模块,然后通过 exportsmodule.exports 来导出需要暴露的接口。

  • 使用 module.exports 对象或exports导出模块对外接口
  • 使用 require(arg) 函数加载模块

🌟加载模块

通过 require(arg) 函数来同步加载所要依赖的其他模块。

require函数接受以下几种参数的传递:

名称 示例
原生模块 如http、fs、path等
相对路径文件模块 如./mod或…/mod
绝对路径文件模块 如/pathtomodule/mod
非原生第三方模块 如mod

require函数用于在当前模块中加载和使用别的模块,传入一个模块名,返回一个模块导出对象。模块名可使用相对路径(以./开头),或者是绝对路径(以/或C:之类的盘符开头)。另外,模块名中的.js扩展名可以省略。

var foo1 = require('./foo');
var foo2 = require('./foo.js');
var foo3 = require('/home/user/foo');
var foo4 = require('/home/user/foo.js');
//foo1 ~ foo4 中保存的是同一个模块的导出对象。

🌟exports 导出模块

exports对象是当前模块的导出对象,用于导出模块公有方法和属性。别的模块通过require函数使用当前模块时得到的就是当前模块的exports对象。以下例子中导出了一个公有方法。

// sum.js
exports.sum = function(a,b){return a+b;
}// main.js
var m = require("./sum");
var num = m.sum(10,20);
console.log(num);

🌟module.exports 导出模块

通过module对象可以访问到当前模块的一些相关信息,但最多的用途是替换当前模块导出对象。例如模块默认导出对象默认是一个普通对象,如果想改为一个函数可以通过如下方式:

导出一个普通函数:

//sum.js
function sum(a,b){return a+b;
}
module.exports= sum;
//main.js
var sum = require('./sum');
sum(10,20);// 30

导出一个构造函数:

//hello.js
function hello(){this.name ="你的名字";this.setName = function(name){this.name = name;}this.sayName = function(){alert(this.name);}
}
module.exports= hello;//main.js
var hello = require('./hello.js');
var o = new hello();
o.setName('张三');
o.sayName(); // 张三

🌟module的其它API

一个JavaScript文件就是一个模块,每一个模块都拥有一个描述自身信息的对象(module)

module对象属性介绍:

API 描述
module.id 模块的ID,通常是当前模块文件路径,含文件名
module.filename 当前模块文件路径,含文件名
module.loaded 判断模块当前是否已加载
module.parent 加载当前脚本的模块对象。
module.children 当前模块加载的模块对象集合,是一个数组

🌟Nodejs模块其他

🌟模块初始化

一个模块中的JS代码仅在模块第一次被使用时执行一次,并在执行过程中初始化模块的导出对象。之后,缓存起来的导出对象被重复利用。

🌟主模块

通过命令行参数传递给NodeJS以启动程序的模块被称为主模块。主模块负责调度组成整个程序的其它模块完成工作。例如通过以下命令启动程序时,main.js就是主模块。

node main.js

🌟变量

🌟__filename

__filename表示当前正在执行的脚本的文件名。输出文件所在位置的绝对路径。

例如:从 /Users/houningzhou/Web/Nodejs/global运行 node test.js:

console.log(__filename);
// 输出: /Users/houningzhou/Web/Nodejs/global/test.js

__filename实际上不是一个全局变量,而是每个模块内部的。

🌟__dirname

__dirname表示当前执行脚本所在的目录

例如:从 /Users/houningzhou/Web/Nodejs/global运行 node test.js:

console.log(__dirname);    
// 输出: /Users/houningzhou/Web/Nodejs/global

__dirname 实际上不是一个全局变量,而是每个模块内部的。

例子,给出两个模块:a 和 b,其中 b 是 a 的一个依赖,目录结构如下:

app/├── a.js              # /Users/mjr/app/a.js└── node_modules/     # /Users/mjr/app/node_modules/b/b.js└──  b/└── b.js

b.js 的 __dirname 返回 /Users/mjr/app/node_modules/b,而 a.js 的 __dirname 返回 /Users/mjr/app

// b.js
(function (exports, require, module, __filename, __dirname) {t = 111;
})();// a.js
(function (exports, require, module, __filename, __dirname) {// ...console.log(t); // 111
})();

🌟node_modules目录

NodeJS定义了一个特殊的node_modules目录用于存放模块。

如果传给 require() 的模块标识符不是核心模块,并且不以 '/''../''./' 开头,则 Node.js 从当前模块的父目录开始,并添加 /node_modules,并尝试加载该位置的模块。

例如某个模块的绝对路径是/home/user/hello.js,在该模块中使用require(‘foo/bar’)方式加载模块时,则NodeJS依次尝试使用以下路径。

/home/user/node_modules/foo/bar  # 当前目录下node_modules目录
/home/node_modules/foo/bar       # 父级目录下node_modules目录
/node_modules/foo/bar            # 根目录下node_modules目录

从当前文件目录开始查找node_modules目录;然后依次进入父目录,查找父目录下的node_modules目录;依次迭代,直到根目录下的node_modules目录。

🌟写在最后

更多Node知识以及API请大家持续关注,尽请期待。各位小伙伴让我们 let’s be prepared at all times!

✨原创不易,还希望各位大佬支持一下!
👍 点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富!