require 函数加载模块原理(被加载的模块会先执行一次)|学习笔记

简介: 快速学习 require 函数加载模块原理(被加载的模块会先执行一次)

开发者学堂课程【Node.js 入门与实战:require函数加载模块原理(被加载的模块会先执行一次)】学习笔记,与课程紧密联系,让用户快速学习知识

课程地址https://developer.aliyun.com/learning/course/588/detail/8297


require 函数加载模块原理(被加载的模块会先执行一次)

 

目录:

一、require 加载模块的作用

二、require 内部执行机制

 

一、require 加载模块的作用

新建文件:a.js,b.js,c.js

一个js文件就是一个模块

//在a.js模块中加载b.js模块

//因为a.js与b.js模块在同一个文件夹,可以直接./b.js

Var b=require(./b.js)

//在b.js模块中添加内容

Function add(x,y){

Return x+y;

}

Var result=add(100,1000)

Console.log(result)

启动运行 a.js

先进入js当前文件夹

控制台输入:node a.js

控制台输出:1100

取消 a.js中的声明变量

Require(./b.js)

重新运行结果与上文一样为1100

执行 reuqire 加载模块时会执行模块所有代码,被加载模块代码全部执行一次

多次在 a.js 模块中加载 b.js 模块

Require(./b.js)

Require(./b.js)

Require(./b.js)

Require(./b.js)

Require(./b.js)

Require(./b.js)

Require(./b.js)

再次控制台运行:

只会打印一行1100

原因在于require加载模块无论文件模块还是核心模块还是第三方模块,会缓存起来,加载直接去缓存中获取,不会重新加载

 image.png

二、require 内部执行机制

从源代码文件中找出 require 函数源码

首先清楚 require,模块是谁的,每一个模块加载时都会被包装成 module

Nodejs.org 官网中的 modules 模块图示:

image.png

任何一个模块加载都会封装为 modules,表示当前模块自身

a. js 中添加代码:

//查找路径

Console.log(module.paths)

代码运行结果:

image.png

逐层查找模块

Require 函数属于 modules 模块中

查看 Module.js 源文件:

Module 内置函数

function -Module(id , -parent)

{this.id=-id;

this.exports=·;this.parent·=-parent;

if-(parent·&&-parent.children){

parent.children.push(this);

this.filename - =-null;

this.loaded-=.false;this.children·--[];

moduLe.exports= Module;

Require 函数源码:

ModuLe.prototype.require-function(path) {assertlpath,"missing path ');

assert(typeof path --- 'string ' , "path must be a string" );return Module._load(path,this,/* isMain*/ false);

};

Module._load = function(request,parent,isMain){

if (parent)·i

debug( 'Module._load ·REQUEST- %s parent: ·%s ' , request, parent.id);.}

-var -filename . =.Module._resolveFilename(request, parent, isMain);

var cachedModule = Module._cache[filename];

//判断缓存,如果有则拿出缓存

if (cachedModule) {

return cachedModule.exports;}

if (NativeModule.nonInternalExists(filename)){debug( 'load native module %s ' , request);

return NativeModule.require(filename);}

var module = new Module(filename,parent);

if (isMain){

process.mainModule = module;module.id - '.';

}

//缓存的键就是路径,若是模块名称这就是模块名称

ModuLe._cache[filename] - module;

//调用try方法加载模块

tryModuleLoad(module,filename);return moduLe.exports;

};

源代码解读:

// 1. module.js

//Loads a module at the given file path. Returns that module 'sl/^exports` property.

//加载给定的模块,并返回该模块中 module.exports中的值

//之所以在每个模块中都能使用require()函数是因为 require函数定义在了每个模块中

ModuLe.prototype.require - function(path){

assert(path,'missing path ');

assert(typeof path --= 'string" , "path must be a string" );

return MoHule._load(path,this,/* isMain*/ false);

};

_load 方法传入模块名称和模块类型

该方法做了5件事:

1.检查 Module._cache 中是否有缓存的模块实例

2.如果缓存中没有,那么创建一个 Moudle 实例

3.将创建的 Module 实例保存到缓存中,供下次使用。

4.调用 module.load()读取模块内容,然后调用 module. compile()编译执行-如果加载解析出错,那么从缓存中删除该模块

5.返回 module.exports

tryModuleLoad方法

// 3.module.js

function tryModuleLoad (module,filename){var threw = true;

try {

//读取 b.js 源文件,读取进来封装和编译执行

module. load(filename);threw - false;

}finally {

if (threw) i

delete ModuLe._cache[filename];

}

}

}

Load方法

//4. module.js

// Given a file name,pass it to the proper extension handler.

//该模块中对要加载的模块进行编译

ModuLe.prototype.load = function(fiLename) {

debug( " load jfor module j',filename,this.id);

assert( !this.loaded);

this.filename = filename;

this.paths = Module._nodeModulePaths(path.dirname(filename));

var extension = path.extname(filename)ll '.js ';

if (!Module._extensions[extension]) extension - '.js ';

ModuLe._extensions[extension](this, ilename);

//此行代码对被加载的模块实现了编译 this.loaded = true;

};

Extension 函数编译模块,需要读取js文件内容

//5. module.js(编译)

//Native extension for .js

Module._extensions[ '.js']- function(module, filename){

//说明 require()加载模块是同步执行的

//同步读取而并非异步

var content = fs.reaTFileSync(filename, : "utf8');

module._compile(internalModule.stripBOM(content), filename);

};

异步读取回到函数:readFile 方法

同步读取:返回值就是文件读取到的结果:readFileSync

编译完毕后执行结果,把当前模块返回出去

Var b=require(./b.js)

最终拿到返回结果,表示在任何函数中都可使用require模块,缓存加载路径命名为键,根据文件路径读取文件,同步读取文件。

相关文章
|
7月前
|
缓存
node中的优先从缓存中加载模块与模块的加载规则
node中的优先从缓存中加载模块与模块的加载规则
|
2月前
|
监控 开发者
确保动态导入模块按正确顺序加载
【10月更文挑战第12天】 在复杂应用开发中,确保动态导入模块按正确顺序加载至关重要,直接影响应用性能、功能和稳定性。本文深入探讨了动态模块加载顺序的影响因素、解决方案及实践案例,提供了详细的策略和方法,帮助开发者有效管理模块加载顺序,提升应用质量。
|
2月前
|
缓存 小程序 UED
如何利用小程序的生命周期函数实现数据的加载和更新?
如何利用小程序的生命周期函数实现数据的加载和更新?
74 4
|
4月前
|
缓存 前端开发 JavaScript
Webpack 模块解析:打包原理、构造形式、扣代码补参数和全局导出
Webpack 模块解析:打包原理、构造形式、扣代码补参数和全局导出
184 1
|
Linux
模块的加载过程四
模块的加载过程四
146 0
|
编译器
模块的加载过程三(下)
模块的加载过程三(下)
177 0
|
存储 Linux C语言
模块的加载过程一
模块的加载过程一
164 0
|
Linux 索引
模块的加载过程三
模块的加载过程三
95 0
|
Linux
模块的加载过程二(上)
模块的加载过程二
104 0
|
程序员 Linux
模块的加载过程二(下)
模块的加载过程二(下)
152 0