Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node基础-require #15

Open
YIngChenIt opened this issue Jun 30, 2020 · 0 comments
Open

Node基础-require #15

YIngChenIt opened this issue Jun 30, 2020 · 0 comments

Comments

@YIngChenIt
Copy link
Owner

你不知道的require

require 内部流程

::: tip
我们先大致了解一下 require()引入一个 .js 或者 .json 文件时候,内部大致是怎么处理的。
:::

    let name = require('./a')
    console.log(name)

上述代码引入 a.js 文件时候,内部大致发生的流程如下:

1、将 ./a 转化为绝对路径,并且补充后缀名(c:\Users\chenying\Desktop\code\a.js)

2、根据绝对路径判断缓存中是否存在缓存的文件,如果存在则取缓存,不存在则继续

3、创建 Module 实例 module,将绝对路径传入

4、取得绝对路径的后缀名,根据后缀名(.js)调用对应的处理函数

5、读 .js.json 文件思路大同小异,通过fs.readFileSync()读取文件内容

6、对读到的 .js 文件的内容外层包裹一个函数,并且将字符串转成函数执行

7、对读到的 .json 文件的内容,转为对象,并且赋值给module.exports

::: tip
我们再来看下源码中对以下几个疑惑点是怎么进行处理的
:::

Module

function Module(id) {
  this.id = id;
  this.exports = {};
  // ..
}

在源码中我们发现 Module 上挂载很多,但是我们真正需要接触到的分别是 idexports,他们分别是绝对路径,和默认导出的空对象

取得绝对路径的后缀名,根据后缀名(.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) 是不想有原型上的属性

通过fs对文件进行读取

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` 上
};

对读到的 .js 文件的内容外层包裹一个函数,并且将字符串转成函数执行

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)

mini 版require的实现

::: tip
我们现在根据步骤结合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);

取得绝对路径的后缀名,根据后缀名(.js)调用对应的处理函数

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`
} 

::: tip
目前已经根据源码思路来实现一款mini版require(),但是还有类似于实现缓存、自动补全后缀名等功能就不贴代码了,可以看下方的源码,欢迎star
:::

源码

点击查看源码

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant