`
天梯梦
  • 浏览: 13741589 次
  • 性别: Icon_minigender_2
  • 来自: 洛杉矶
社区版块
存档分类
最新评论

Node.js:模块查找,引用及缓存机制

 
阅读更多

1. Node.js的模块载入方式与机制

Node.js中模块可以通过文件路径或名字获取模块的引用。模块的引用会映射到一个js文件路径,除非它是一个Node内置模块。Node的内置模块公开了一些常用的API给开发者,并且它们在Node进程开始的时候就预加载了。

 

其它的如通过NPM安装的第三方模块(third-party modules)或本地模块(local modules),每个模块都会暴露一个公开的API。以便开发者可以导入。如

var mod = require('module_name')

 

此句执行后,Node内部会载入内置模块或通过NPM安装的模块。require函数会返回一个对象,该对象公开的API可能是函数,对象,或者属性如函数,数组,甚至任意类型的JS对象。

 

这里列下node模块的载入及缓存机制

  1. 载入内置模块(A Core Module)
  2. 载入文件模块(A File Module)
  3. 载入文件目录模块(A Folder Module)
  4. 载入node_modules里的模块
  5. 自动缓存已载入模块

 

一、载入内置模块

Node的内置模块被编译为二进制形式,引用时直接使用名字而非文件路径。当第三方的模块和内置模块同名时,内置模块将覆盖第三方同名模块。因此命名时需要注意不要和内置模块同名。如获取一个http模块

var http = require('http')

 

返回的http即是实现了HTTP功能Node的内置模块。

 

二、载入文件模块

绝对路径的

var myMod = require('/home/base/my_mod')

 

或相对路径的

var myMod = require('./my_mod')

 

注意,这里忽略了扩展名“.js”,以下是对等的

var myMod = require('./my_mod')
var myMod = require('./my_mod.js')

 

 

三、载入文件目录模块

可以直接require一个目录,假设有一个目录名为folder,如

var myMod = require('./folder')

 

此 时,Node将搜索整个folder目录,Node会假设folder为一个包并试图找到包定义文件package.json。如果folder 目录里没有包含package.json文件,Node会假设默认主文件为index.js,即会加载index.js。如果index.js也不存在, 那么加载将失败。

 

假如目录结构如下

Nodejs:模块查找,引用及缓存机制

package.json定义如下

{
    "name": "pack",
    "main": "modA.js"
}

 

此时 require('./folder') 将返回模块modA.js。如果package.json不存在,那么将返回模块index.js。如果index.js也不存在,那么将发生载入异常。

 

四、载入node_modules里的模块

如果模块名不是路径,也不是内置模块,Node将试图去当前目录的node_modules文件夹里搜索。如果当前目录的node_modules里没有找到,Node会从父目录的node_modules里搜索,这样递归下去直到根目录。

不必担心,npm命令可让我们很方便的去安装,卸载,更新node_modules目录。

 

五、自动缓存已载入模块

对于已加载的模块Node会缓存下来,而不必每次都重新搜索。下面是一个示例

 

modA.js

console.log('模块modA开始加载...')
exports = function() {
    console.log('Hi')
}
console.log('模块modA加载完毕')

 

init.js

var mod1 = require('./modA')
var mod2 = require('./modA')
console.log(mod1 === mod2)

 

命令行执行:

node init.js

 

 

输入如下

Nodejs:模块查找,引用及缓存机制

可以看到虽然require了两次,但modA.js仍然只执行了一次。mod1和mod2是相同的,即两个引用都指向了同一个模块对象。

 

参考:http://www.cnblogs.com/snandy/p/3445550.html

 

2. nodejs 模块查找

Nodejs:模块查找,引用及缓存机制

nodejs在加载外部自定义模块时对模块有查找顺序,找到后还会进行缓存。

 

查找顺序:

1. 相对路径,比如提供./ 或者../这种以'./'和‘..’开始的路径,简单的,就是相对当前位置的路径。

2.绝对路径,这时候将按以下顺序查找:

在 进入路径查找之前有必要描述一下module path这个Node.js中的概念。对于每一个被加载的文件模块,创建这个模块对象的时候,这个模块便会有一个paths属性,其值根据当前文件的路径 计算得到。我们创建modulepath.js这样一个文件,其内容为:

console.log(module.paths);

 

我们将其放到任意一个目录中执行node modulepath.js命令,将得到以下的输出结果。

[ '/home/ikeepstudying/research/node_modules',
'/home/ikeepstudying/node_modules',
'/home/node_modules',
'/node_modules' ]

 

Windows下:

[ 'c:\\nodejs\\node_modules', 'c:\\node_modules' ]

然后是['.']

 

然后是:

windows下%NODE_PATH%,%USERPROFILE%/.node_modules, %USERPROFILE%/.node_libraries

非windows下$NODE_PATH, $HOME/.node_modules, $HOME/.node_libraries

[NODE_PATH,HOME/.node_modules,HOME/.node_libraries,execPath/../../lib/node]

 

然后是node.exe目录的../../lib/node,所以这个具体取决于node二进制文件放哪里.

Nodejs:模块查找,引用及缓存机制

简而言之,如果require绝对路径的文件,查找时不会去遍历每一个node_modules目录,其速度最快。其余流程如下:

  1. 从module path数组中取出第一个目录作为查找基准。
  2. 直接从目录中查找该文件,如果存在,则结束查找。如果不存在,则进行下一条查找。
  3. 尝试添加.js、.json、.node后缀后查找,如果存在文件,则结束查找。如果不存在,则进行下一条。
  4. 尝试将require的参数作为一个包来进行查找,读取目录下的package.json文件,取得main参数指定的文件。
  5. 尝试查找该文件,如果存在,则结束查找。如果不存在,则进行第3条查找。
  6. 如果继续失败,则取出module path数组中的下一个目录作为基准查找,循环第1至5个步骤。
  7. 如果继续失败,循环第1至6个步骤,直到module path中的最后一个值。
  8. 如果仍然失败,则抛出异常。

整个查找过程十分类似原型链的查找和作用域的查找。所幸Node.js对路径查找实现了缓存机制,否则由于每次判断路径都是同步阻塞式进行,会导致严重的性能消耗。

 

一旦加载成功就以模块的路径进行缓存,这里有一个陷阱。

 

就是如果父目录包含X模块,且存在引用X模块的代码。而子目录也是相同的情况。那么父目录和子目录下实际引用到的分别是自己目录下的那个X模块,而不是之前那个的复用。也就是要注意他缓存是匹配全路径的。

 

Nodejs:模块查找,引用及缓存机制
Nodejs:模块查找,引用及缓存机制

 

 

3. 利用nodejs模块缓存机制创建“全局变量”

在《深入浅出nodejs》有这样一段(有部分增减):

nodejs引入模块分四个步骤 路径分析 文件定位 编译执行 加入内存 核心模块部分在node源代码的编译过程中就编译成了二级制文件,在node启动时就直接加载如内存,所以这部分模块引入时,前三步省略,直接加入。 nodejs的模块加载和浏览器js加载一样都有缓存机制,不同的是,浏览器仅仅缓存文件,而nodejs缓存的是编译和执行后的对象(缓存内存)。 基于以上三点:我们可以编写一个模块,用来记录长期存在的变量。例如:我可以编写一个记录接口访问数的模块:

var count = {}; // 因模块是封闭的,这里实际上借用了js闭包的概念
exports.count = function(name){
     if(count[name]){
          count[name]++;
     }else{
          count[name] = 1;
     }
     console.log(name + '被访问了' + count[name] + '次。');
};

 

我们在路由里这样引用:

var count = require('count');

export.index = function(req, res){
    count('index');
};

 

以上便完成了对接口调用数的统计,但这只是个demo,因为数据存储在内存,服务器重启后便会清空。真正的计数器一定是要结合持久化存储器的。

 

4. nodejs清除require缓存 delete require.cache

开发nodejs应用时会面临一个麻烦的事情,就是修改了配置数据之后,必须重启服务器才能看到修改后的结果。

 

于是问题来了,挖掘机哪家强?噢,no! no! no!

 

怎么做到修改文件之后,自动重启服务器。

 

server.js中的片段:

var port = process.env.port || 1337;
app.listen(port);
console.log("server start in " + port);
exports.app = app;

 

假定我们现在是这样的:

app.js的片段:

var app = require('./server.js');

 

如果我们在server.js中启动了服务器,我们停止服务器可以在app.js中调用

app.app.close()

 

但是当我们重新引入server.js

app =  require('./server.js')

 

的时候会发现并不是用的最新的server.js文件,原因是require的缓存机制,在第一次调用require('./server.js')的时候缓存下来了。

 

这个时候怎么办?

下面的代码解决了这个问题:

delete require.cache[require.resolve('./server.js')];
app = require('./server.js');

 

参考:http://www.xiwnn.com/article/UTWpN.html

 

原文转自:Nodejs:模块查找,引用及缓存机制

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    Node.js+开发指南

    6.1.3 通过查找node_modules目录加载模块 133 6.1.4 加载缓存 134 6.1.5 加载顺序 134 6.2 控制流 135 6.2.1 循环的陷阱 135 6.2.2 解决控制流难题 137 6.3 Node.js应用部署 138 6.3.1 日志...

    node.js手册中文版

    模块加载过程中还涉及缓存机制,一旦模块被加载,其结果会被缓存,避免重复加载。 #### 进程(Process) `process`对象提供了与当前Node.js进程交互的方法和属性,如监听`exit`和`uncaughtException`事件,获取和...

    Node.js-node.js模拟实现一个简单的文件系统

    Node.js是一个基于Chrome V8引擎的JavaScript运行环境,它允许我们在服务器端编写高性能的应用程序,其中就包括文件系统的操作。 **Node.js的文件系统模块** 在Node.js中,我们主要使用内置的`fs`(文件系统)模块...

    node.js v0.5.7官方API文档

    - Node.js会按照特定的顺序查找模块,首先是当前目录下的`node_modules`文件夹。 - 如果没有找到,则会继续向上级目录搜索`node_modules`文件夹。 4. **将文件夹作为模块**: - 可以将整个文件夹作为模块,只需...

    node-v12_nodev12.18.3_Node.js_源码.zip

    在源码中,我们可以研究模块的加载机制,包括如何查找和加载模块,以及如何实现模块的缓存,从而理解Node.js的依赖管理和模块化设计。 四、进程与线程 Node.js提供了一套进程和线程相关的API,如process模块,用于...

    解读node.js-api文档.pdf

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它允许开发者在服务器端使用 JavaScript 编程。本文档主要解读的是 Node.js 的 API 文档,涵盖了一些核心的全局对象、控制台操作、模块系统以及计时器等...

    学习Node.js模块机制

    3. **缓存机制**:Node.js会对所有加载过的模块进行缓存,包括核心模块和文件模块,以避免重复加载,提高性能。 4. **模块查找规则**:对于不同类型的模块标识,Node.js有不同的查找策略。核心模块、相对路径、绝对...

    从零开始学习Node.js系列教程之设置HTTP头的方法示例

    Node.js是一种基于Chrome V8引擎的JavaScript运行时环境,它使用事件驱动、非阻塞I/O模型,使得JavaScript能够直接运行在服务器上,这让Node.js成为构建快速、可扩展网络应用的一个非常好的选择。在Node.js中设置...

    详细分析Node.js 模块系统

    当使用require加载一个模块时,Node.js会首先从文件模块缓存中查找是否有该模块的缓存实例。如果缓存中存在,则直接返回缓存实例,否则会按照一定的规则从文件系统中加载模块文件。 Node.js模块的查找规则如下: 1....

    node.js require() 源码解读

    `require()` 是 Node.js 中用于加载和使用模块的关键方法,它遵循特定的查找规则来确定并执行所需模块。 1. **内置模块**:如果 `require()` 的参数是内置模块(如 `require('http')`),Node.js 直接返回该模块的...

    module.js

    2. 缓存机制:Node.js在加载模块后,会将其缓存起来,以提高后续的加载速度。当再次请求同一模块时,将直接从缓存中获取,避免重复解析和执行。 三、模块的导出与导入 1. `exports`对象:每个模块都有一个`exports...

    Node.JS_practice

    2. 第三方模块:介绍npm(Node.js包管理器)的使用,如何查找、安装、管理和发布第三方模块。 3. CommonJS规范:解析Node.js中的require()和module.exports,理解模块间的依赖关系。 三、Node.js的异步编程 1. 回调...

    2019Node.js API 中文版

    Node.js会在当前目录下的`node_modules`文件夹查找模块,如果没有找到,则会沿着父目录继续查找。 - **从全局文件夹加载模块** 全局安装的模块可以从全局文件夹加载。 - **循环** 模块之间可以互相引用,但必须...

    Node.js学习教程之Module模块

    5. **node_modules**:在当前目录及其父目录下查找。 **关于缓存** 模块加载后会存储在`require.cache`中,可以通过此对象查看和操作已缓存的模块。 **exports与module.exports** `exports`和`module.exports`的...

    Node.js-friendpm通过LAN和P2P网络与朋友分享缓存中的所有node包

    Node.js 是一个基于Chrome V8引擎的JavaScript运行环境,它使得开发者可以使用JavaScript在服务器端进行编程。在这个环境中,`friendpm`是一个独特的项目,它允许用户通过局域网(LAN)和对等网络(P2P)技术来分享...

    node.js中module模块的功能理解与用法实例分析

    - 使用缓存机制时,要注意可能导致的内存占用问题,及时释放不再使用的模块。 - 核心模块和文件模块的加载方式略有不同,需要根据实际情况选择合适的模块类型和引用方式。 以上详细介绍了Node.js中module模块的功能...

    nodejs-src

    在`node-v0.10.28`中,模块加载机制主要由`module.js`文件定义,包括了模块的查找、加载和缓存等过程。通过`require()`函数,我们可以方便地引入和使用其他模块。源码中的`Module._load`方法是加载模块的核心逻辑,...

    nodejs手册中文版.pdf

    加载过程中,Node.js会查找并缓存模块,这样就可以提高模块的加载速度。开发者不应修改require.paths,因为这并不会影响Node.js的加载行为,且在require.paths中放置相对路径是不合适的。 4. **核心模块**:Node.js...

    浅谈Node模块系统及其模式

    Node.js的模块系统是其核心特性之一,它允许开发者组织代码并实现代码的复用与隔离,有效地解决了JavaScript中全局命名空间污染的问题。模块系统基于CommonJS标准,但Node.js在其基础上进行了扩展。 首先,我们要...

Global site tag (gtag.js) - Google Analytics