咱们先大体了解一下 require()引入一个 `.js` 或者 `.json` 文件时候,内部大体是怎么处理的。 node
let name = require('./a')
console.log(name)
复制代码
上述代码引入 a.js
文件时候,内部大体发生的流程以下:git
一、将 ./a
转化为绝对路径,而且补充后缀名(c:\Users\chenying\Desktop\code\a.js
)github
二、根据绝对路径判断缓存中是否存在缓存的文件,若是存在则取缓存,不存在则继续json
三、建立 Module
实例 module
,将绝对路径传入缓存
四、取得绝对路径的后缀名,根据后缀名(.js
)调用对应的处理函数app
五、读 .js
和 .json
文件思路大同小异,经过fs.readFileSync()读取文件内容函数
六、对读到的 .js
文件的内容外层包裹一个函数,而且将字符串转成函数执行ui
七、对读到的 .json
文件的内容,转为对象,而且赋值给module.exports
this
::: tip 咱们再来看下源码中对如下几个疑惑点是怎么进行处理的 :::spa
Module
类function Module(id) {
this.id = id;
this.exports = {};
// ..
}
复制代码
在源码中咱们发现 Module
上挂载不少,可是咱们真正须要接触到的分别是 id
和 exports
,他们分别是绝对路径,和默认导出的空对象
.js
)调用对应的处理函数Module._extensions = Object.create(null); //建立一个对象
Module._extensions['.js'] = function(module, filename) {}; // 在对象上挂载 `.js`的处理函数
Module._extensions['.json'] = function(module, filename) {}; // 在对象上挂载 `.json`的处理函数
Module._extensions[extension](this, filename); // 根据绝对路径的后缀名,调用挂载在对象上相应的方法
复制代码
用 Object.create(null)
是不想有原型上的属性
Module._extensions['.js'] = function(module, filename) {
var content = fs.readFileSync(filename, 'utf8'); // 读取文件内容
module._compile(stripBOM(content), filename); // 对读取的内容包裹一层函数,而且转为函数自执行,让用户本身把想要导出的内容挂载到 `module.exports` 上面去
};
Module._extensions['.json'] = function(module, filename) {
var content = fs.readFileSync(filename, 'utf8');
module.exports = JSON.parse(stripBOM(content)); // 读到的json文件直接挂载到 `module.exports` 上
};
复制代码
let wrap = function(script) {
return Module.wrapper[0] + script + Module.wrapper[1];
};
const wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
];
const wrapper = Module.wrap(content);
compiledWrapper = vm.runInThisContext(wrapper)
compiledWrapper.call(this.exports, this.exports, require, this,filename, dirname)
复制代码
咱们如今根据步骤结合node来实现一款require()加深理解
module
实例,将module.exports导出let path = require('path');
let fs = require('fs');
let vm = require('vm');
function Module(id){
this.id = id;
this.exports = {}
}
function myRequire(filePath){
let absPath = path.resolve(__dirname,filePath); // 把当前的filePath变成一个绝对路径
let module = new Module(absPath);
module.load(); // 加载模块
return module.exports
}
let r = myRequire('./a.js');
console.log(r);
复制代码
Module._extensions = {};
Module.prototype.load = function(){
let ext = path.extname(this.id); // 取出当前实例上挂载的绝对路径,获取后缀名
Module._extensions[ext](this) // 根据后缀名调用对应的处理函数
}
复制代码
Module._extensions
上补充对应的处理函数let wrapper = [
'(function(exports,module,require,__dirname,__filename){'
,
'})'
]
Module._extensions['.js'] = function(module){
let script = fs.readFileSync(module.id,'utf8'); //读取文件
let functStr = wrapper[0] + script + wrapper[1]; // 字符串包裹
let fn = vm.runInThisContext(functStr); //将字符串转为函数
fn.call(module.exports,module.exports,module,myRequire);//执行函数
}
Module._extensions['.json']= function(module){
let script = fs.readFileSync(module.id,'utf8');//读取文件
module.exports = JSON.parse(script); // 将读取到的json挂载到 `module.exports`
}
复制代码
目前已经根据源码思路来实现一款mini版require(),可是还有相似于实现缓存、自动补全后缀名等功能就不贴代码了,能够看下方的源码,欢迎star
文章可能有不足的地方,请你们见谅,若是有什么疑问能够下方留言讨论。