# require
# 模拟 require 简单功能
function $require(id) {
const fs = require('fs')
const path = require('path')
const filename = path.join(__dirname, id)
const dirname = path.dirname(filename)
// 同步读取文件
let code = fs.readFileSync(filename, 'utf8')
// 执行代码,需要营造一个私有空间,将全局变量传入进去,这里只传部分测试
let module = {
id: filename,
exports: {}
}
let exports = module.exports
// 这里也可以使用 new Function 来实现,传入的最后一个参数为函数体内部字符,不需要 eval
// 1. 字符串拼接 function
code = `
(function ($require, module, exports, __dirname, __filename) {
${code}
})($require, module, exports, dirname, filename)
`
// 执行代码
eval(code)
// 返回读取文件导出的内容
return module.exports
// 2. 使用new Function 实现
// let fn = new Function('$require', 'module', 'exports', '__dirname', '__filename', code + '\n return module.exports')
// return fn($require, module, exports, dirname, filename, code)
}
// use
var test = $require('./test1.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
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
# require 加载机制
require 加载文件时可以省略拓展名:
// 第一种不省略相对路径
require('./module') // 省略拓展名
// 第一步按 js 文件加载 require('./module.js')
// 第二步按 json 文件加载 require('./module.json')
// 第三步按预编译好的 c++ 文件加载 require('./module.node')
// 第四步当作文件夹找文件夹下的 package.json 文件 main 属性指向文件
// 第五步找文件夹下的 index.[js|json|node]
1
2
3
4
5
6
7
2
3
4
5
6
7
省略 ./ 或 /
// 找核心模块
// 按目录向上依次去找 node_modules,越近的优先级越高
1
2
2
# 模块的缓存
目的是为了加速代码执行,一般不会处理,如果需要避免缓存,可以导出方法
- 第一次加载某个模块时,node 会缓存该模块,以后在加载该模块就直接从缓存取出该模块
- 如果需要多次执行模块中的代码,一般可以让模块暴露行为(函数)
- 模块的缓存可以通过 require.cache 拿到,同样也可以删除
// 模块
module.exports = {
date: new Date()
}
// 入口文件
var pre
setInterval(() => {
// console.log(require.cache) // 缓存对象,类似 module 对象
// 删除缓存
// Object.keys(require.cache).forEach(key => {
// delete require.cache[key]
// })
var a = require('./test')
console.log(pre === a) // 第一次打印 false,之后都是 true
pre = a
console.log(a.date) // 发现每次打印的时间相同
}, 1000)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 增加 require 的缓存机制
function $require(id) {
const fs = require('fs')
const path = require('path')
const filename = path.join(__dirname, id)
// 如果有缓存,直接返回缓存中的数据
$require.cache = $require.cache || {}
if ($require.cache[filename]) {
return $require.cache[filename].exports
}
const dirname = path.dirname(filename)
// 同步读取文件
let code = fs.readFileSync(filename, 'utf8')
// 执行代码,需要营造一个私有空间,将全局变量传入进去,这里只传部分测试
let module = {
id: filename,
exports: {}
}
let exports = module.exports
code = `
(function ($require, module, exports, __dirname, __filename) {
${code}
})($require, module, exports, dirname, filename)
`
// 执行代码
eval(code)
// 第一次,没有缓存,将 module 记录到缓存中
$require.cache[filename] = module
// 返回读取文件导出的内容
return module.exports
}
// use
var test = $require('./test1.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
33
34
35
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