浅析node.js的模块加载机制

2020-06-17 06:24:19易采站长站整理

使用exports导出模块


module.exports = {
name: 'Tom'
};

引用的名称可以不带路径,若不带路径表示引入的是node提供的模块或是npm安装的第三方模块(node_modules)

模块定义

module对象:在每一个模块中,module对象代表该模块自身。

export属性:module对象的一个属性,它向外提供接口。

模块标识

模块标识指的是传递给require方法的参数,必须是符合小驼峰命名的字符串,或者以 .、..、开头的相对路径,或者绝对路径。

node中模块解析流程

首先接收参数,把传入的模块名称解析成绝对路径
若没有后缀名称,依次拼接.js .json .node尝试加载,仍到不到模块则报错
取得正确的路径后判断缓存中是否存在此模块,若有则取出
若缓存中不存在则加载此文件,在外包裹一层闭包并执行它

以上为大致流程,下面尝试着写一下模块。

代码的基本结构:


/**
* Module类,用于处理模块加载
*/
function Module() {}

//模块的缓存
Module._cacheModule = {};

//不同扩展名的加载策略
Module._extensions = {};

//根据moduleId解析绝对路径,
Module._resolveFileName = function(moduleId) {};

//入口函数
function req(moduleId) {}

附上全部代码:


const path = require('path');
const fs = require('fs');
const vm = require('vm');

/**
* Module类,用于处理模块加载
*/
function Module(file) {
this.id = file; //当前模块的id,它使用完整的绝对路径标识,因此是唯一的
this.exports = {}; //导出
this.loaded = false; //模块是否已加载完毕
}

//模块的缓存
Module._cacheModule = {};

Module._wrapper = ['(function(exports,require,module,__dirname,__filename){', '});'];

//不同扩展名的加载策略
Module._extensions = {
'.js': function(currentModule) {
let js = fs.readFileSync(currentModule.id, 'utf8'); //读取出js文件内容
let fn = Module._wrapper[0] + js + Module._wrapper[1];
vm.runInThisContext(fn).call(
currentModule.exports,
currentModule.exports,
req,
currentModule,
path.dirname(currentModule.id),
currentModule.id);
return currentModule.exports;
},
'.json': function(currentModule) {
let json = fs.readFileSync(currentModule.id, 'utf8');
return JSON.parse(json); //转换为JSON对象返回
},
'.node': ''
};

//加载模块(实例方法)
Module.prototype.load = function(file) {
let extname = path.extname(file); //获取后缀名
return Module._extensions[extname](this);