node学习笔记——自己实现一个require

Node 使用 CommonJS 模块规范,内置的 require 函数用具加载模块文件。

require 的基本功能是,读入并执行一个 JavaScript 文件,然后返回该模块的 export 对象,如果没有发现指定模块,会报错。内置 require 加载文件时可以省略扩展名,默认按JS文件执行。

简单实现 require 加载机制

为了弄清楚 commom.js 的运行机制,我们可以自己简单实现一下内部的模块加载机制:

commomModule.js如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

//'use strict'
//自己写一个require函数
function $require(id){
//1.先找到文件,如果文件不存在,can't find module '....'
//2.读取文件内容, 内容是js代码
const fs = require('fs');
const path = require('path');

//要加载的js文件路径,完成路径
const filename = path.join(__dirname, id);
//去除文件名,获取文件夹路径
const dirname = path.dirname(filename);
let code = fs.readFileSync(filename, 'utf8'); //不会进入事件队列
//3.执行代码,所需要执行的代码,需要营造一个私有空间
let module = {id:filename,exports:{}};
let exports = module.exports;

code = `(function($require,module,exports,__dirname,__filename){
${code}
})($require,module,exports,dirname,filename);`;

eval(code); //执行一下加载过来的code自执行函数

//4.返回值
return module.exports;
}

var m1 = $require('./module1.js');

m1.a.say();
m1.b.say();

module1.js:

1
2
3
4
5
6
7
8
9
10
var module2 = $require('./module2.js');

module.exports = {
a:{
say: () => {
console.log("module1 say")
}
},
b: module2
}

module2.js:

1
2
3
4
5
module.exports = {
say: () => {
console.log('module2 say');
}
}

输出结果为:

1
2
module1 say
module2 say

以上就是 node 模块机制 require 的简单实现。

模块的缓存

第一次加载某个模块时,Node 会缓存该模块,以后再加载该模块,就直接从缓存中取出该模块的 module.exports 属性,不会再执行该模块

如果需要多次执行模块中的代码,一般可以让模块暴露行为(函数)

模块的缓存可以通过 require.cache 拿到,同样也可以删除

例如定时删除缓存:

1
2
3
Object.keys(require.cache).forEach((key) => {
delete require.cache[key];
})

在上面自定义的 require 中简单实现缓存机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

//'use strict'
//自己写一个require函数
function $require(id){
//1.先找到文件,如果文件不存在,can't find module '....'
//2.读取文件内容, 内容是js代码
const fs = require('fs');
const path = require('path');

//要加载的js文件路径,完成路径
const filename = path.join(__dirname, id);

//有则赋值,没有则创建一个对象
$require.cache = $require.cache || {};
if($require.cache[filename]){
//存在缓存则直接返回缓存对象的exports
return require.cache[filename].exports;
}

//去除文件名,获取文件夹路径
const dirname = path.dirname(filename);
let code = fs.readFileSync(filename, 'utf8'); //不会进入事件队列
//3.执行代码,所需要执行的代码,需要营造一个私有空间
let module = {id:filename,exports:{}};
let exports = module.exports;

code = `(function($require,module,exports,__dirname,__filename){
${code}
})($require,module,exports,dirname,filename);`;

eval(code); //执行一下加载过来的code自执行函数

//缓存起来
$require.cache[filename] = module;

//4.返回值
return module.exports;
}

var m1 = $require('./module1.js');

m1.a.say();
m1.b.say();



完~