Node.js require 模块加载原理 All In One

require 加载模块,搜索路径


"use strict"; /**
*
* @author xgqfrms
* @license MIT
* @copyright xgqfrms
* @created 2020-10-01
* @modified
*
* @description
* @difficulty Easy Medium Hard
* @complexity O(n)
* @augments
* @example
* @link
* @solutions
*
* @best_solutions
*
*/ const log = console.log; function Module(id, parent) {
this.id = id;
this.path = id;
// path = (id - filename.js)
this.paths = [];
// [paths1, parent path, ..., root path]
// paths1 = (id - filename.js) + node_modules
this.exports = {};
this.parent = parent;
this.filename = null;
this.loaded = false;
this.children = [];
} // 在原型上添加只有实例才可以访问的实例方法 require
Module.prototype.require = function(path) {
// 调用构造函数的静态方法 Module._load
return Module._load(path, this);
}; // 用模块的绝对路径,那么除了 module.filename
// Node 还提供一个 require.resolve 方法,供外部调用,用于从模块名取到绝对路径
require.resolve = function(request) {
return Module._resolveFilename(request, self);
}; Module.prototype.load = function(filename) {
var extension = path.extname(filename) || '.js';
if (!Module._extensions[extension]) {
extension = '.js';
}
Module._extensions[extension](this, filename);
this.loaded = true;
}; // 首先,将模块文件读取成字符串;
// 然后剥离 utf8 编码特有的 BOM 文件头,最后编译该模块.
Module._extensions['.js'] = function(module, filename) {
var content = fs.readFileSync(filename, 'utf8');
module._compile(stripBOM(content), filename);
}; Module._extensions['.json'] = function(module, filename) {
var content = fs.readFileSync(filename, 'utf8');
try {
module.exports = JSON.parse(stripBOM(content));
} catch (err) {
err.message = filename + ': ' + err.message;
throw err;
}
}; Module.prototype._compile = function(content, filename) {
var self = this;
var args = [self.exports, require, self, filename, dirname];
return compiledWrapper.apply(self.exports, args);
} // 上面的代码基本等同于下面的形式
// (function (exports, require, module, __filename, __dirname) {
// // 模块源码
// }); Module._load = function(request, parent, isMain) {
// 计算绝对路径
var filename = Module._resolveFilename(request, parent);
// 第一步:如果有缓存,取出缓存
var cachedModule = Module._cache[filename];
if (cachedModule) {
return cachedModule.exports;
}
// 第二步:是否为内置模块
if (NativeModule.exists(filename)) {
return NativeModule.require(filename);
}
// 第三步:生成模块实例,存入缓存
var module = new Module(filename, parent);
// 实例化 module
Module._cache[filename] = module;
// 第四步:加载模块
try {
module.load(filename);
hadException = false;
} finally {
if (hadException) {
delete Module._cache[filename];
}
}
// 第五步:输出模块的exports属性
return module.exports;
} // Module._cache = []; Module._resolveFilename = function(request, parent) {
// 第一步:如果是内置模块,不含路径返回
if (NativeModule.exists(request)) {
return request;
}
// 第二步:确定所有可能的路径
var resolvedModule = Module._resolveLookupPaths(request, parent);
var id = resolvedModule[0];
var paths = resolvedModule[1];
// 第三步:确定哪一个路径为真
var filename = Module._findPath(request, paths);
if (!filename) {
var err = new Error("Cannot find module '" + request + "'");
err.code = 'MODULE_NOT_FOUND';
throw err;
}
return filename;
}; Module._resolveLookupPaths = function(request, parent){
// 列出可能的路径
} // 确认哪一个路径为真
Module._findPath = function(request, paths) {
// 列出所有可能的后缀名:.js,.json, .node
var exists = Object.keys(Module._extensions);
// 如果是绝对路径,就不再搜索
if (request.charAt(0) === '/') {
paths = [''];
}
// 是否有后缀的目录斜杠
var trailingSlash = (request.slice(-1) === '/');
// 第一步:如果当前路径已在缓存中,就直接返回缓存
var cacheKey = JSON.stringify({request: request, paths: paths});
if (Module._pathCache[cacheKey]) {
return Module._pathCache[cacheKey];
}
// 第二步:依次遍历所有路径
for (var i = 0, PL = paths.length; i < PL; i++) {
var basePath = path.resolve(paths[i], request);
var filename; if (!trailingSlash) {
// 第三步:是否存在该模块文件
filename = tryFile(basePath);
if (!filename && !trailingSlash) {
// 第四步:该模块文件加上后缀名,是否存在
filename = tryExtensions(basePath, exists);
}
}
// 第五步:目录中是否存在 package.json
if (!filename) {
filename = tryPackage(basePath, exists);
}
if (!filename) {
// 第六步:是否存在目录名 + index + 后缀名
filename = tryExtensions(path.resolve(basePath, 'index'), exists);
}
// 第七步:将找到的文件路径存入返回缓存,然后返回
if (filename) {
Module._pathCache[cacheKey] = filename;
return filename;
}
}
// 第八步:没有找到文件,返回false
return false;
}; module.exports = Module; // 模块的加载实质上
// 注入 exports、require、module 三个全局变量;
// 然后执行模块的源码;
// 最后将模块的 exports 变量的值输出.

exports = module.exports

module.exports 与 exports 指向同一个Object 引用

https://blog.tableflip.io/the-difference-between-module-exports-and-exports

"use strict";

/**
*
* @author xgqfrms
* @license MIT
* @copyright xgqfrms
* @created 2020-10-01
* @modified
*
* @description
* @difficulty Easy Medium Hard
* @complexity O(n)
* @augments
* @example
* @link
* @solutions
*
* @best_solutions
*
*/ const log = console.log; // module.exports 与 exports 指向同一个Object 引用 // SyntaxError: Identifier 'module' has already been declared
// module = {
// exports: {},
// //others
// };
// // {exports: {…}} // module.exports;
// // {} // exports = module.exports;
// // {} // exports.a = `a`; // module.exports.a = `aa`;
// module.exports.b = `b`; // log(`exports =`, exports);
// log(`module.exports =`, module.exports); /* exports = { a: 'aa', b: 'b' }
module.exports = { a: 'aa', b: 'b' } */ const test = () => {
const module = {
exports: {},
//others
};
// {exports: {…}}
module.exports;
// {}
const exports = module.exports;
// {}
exports.a = `a`;
module.exports.a = `aa`;
module.exports.b = `b`;
log(`exports =`, exports);
log(`module.exports =`, module.exports);
}
test(); /* exports = { a: 'aa', b: 'b' }
module.exports = { a: 'aa', b: 'b' } */

node_modules

  paths: [
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/module.exports/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/node_modules',
'/Users/xgqfrms-mbp/Documents/node_modules',
'/Users/xgqfrms-mbp/node_modules',
'/Users/node_modules',
'/node_modules'
]

➜ module.exports git:(master) ✗ node test.js
exports = {}
module.exports = [Function: app] { test: 'abc' }
module Module {
id: '/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/module.exports/app.js',
path: '/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/module.exports',
exports: [Function: app] { test: 'abc' },
parent: Module {
id: '.',
path: '/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/module.exports',
exports: {},
parent: null,
filename: '/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/module.exports/test.js',
loaded: false,
children: [ [Circular] ],
paths: [
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/module.exports/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/node_modules',
'/Users/xgqfrms-mbp/Documents/node_modules',
'/Users/xgqfrms-mbp/node_modules',
'/Users/node_modules',
'/node_modules'
]
},
filename: '/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/module.exports/app.js',
loaded: false,
children: [],
paths: [
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/module.exports/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/node_modules',
'/Users/xgqfrms-mbp/Documents/node_modules',
'/Users/xgqfrms-mbp/node_modules',
'/Users/node_modules',
'/node_modules'
]
} app = [Function: app] { test: 'abc' } undefined
args = []
test = abc

path & paths

function Module(id, parent) {
this.id = id;
this.path = path;// id - filename.js
this.paths = [path, parent path, ..., root path];
this.exports = {};
this.parent = parent;
this.filename = null;
this.loaded = false;
this.children = [];
} module.exports = Module;

var module = new Module(filename, parent);

demo


const log = console.log; // module.js log('module.id: ', module.id);
log('module.exports: ', module.exports);
log('module.parent: ', module.parent);
log('module.filename: ', module.filename);
log('module.loaded: ', module.loaded);
log('module.children: ', module.children);
log('module.paths: ', module.paths); log(`\nmodule =`, module); /* $ node module.js module.id: .
module.exports: {}
module.parent: null
module.filename: /Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/module.js
module.loaded: false
module.children: []
module.paths: [
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/node_modules',
'/Users/xgqfrms-mbp/Documents/node_modules',
'/Users/xgqfrms-mbp/node_modules',
'/Users/node_modules',
'/node_modules'
] */ /* module = Module {
id: '.',
path: '/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system',
exports: {},
parent: null,
filename: '/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/module.js',
loaded: false,
children: [],
paths: [
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/module-system/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/src/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/learn-node.js-by-practice/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/node_modules',
'/Users/xgqfrms-mbp/Documents/node_modules',
'/Users/xgqfrms-mbp/node_modules',
'/Users/node_modules',
'/node_modules'
]
} */

CommonJS

https://en.wikipedia.org/wiki/CommonJS

require() function & module.exports (alias, exports)


default exports

module.exports = xxx;

module.exports.xxx = xxx;
module.exports = {
xxx: xxx,
};
"use strict";

/**
*
* @author xgqfrms
* @license MIT
* @copyright xgqfrms
* @created 2020-10-01
* @modified
*
* @description
* @difficulty Easy Medium Hard
* @complexity O(n)
* @augments
* @example
* @link
* @solutions
*
* @best_solutions
*
*/ const log = console.log; const app = (datas = [], debug = false) => {
log(`datas =`, datas)
}; //
// exports = app; // module export (const { app, } = require(`./app`);)
// exports.app = app; // default export (const app = require(`./app`);)
module.exports = app; //
// module.exports.app = app; //
// module.exports = {
// app,
// };

module.exports 覆盖 exports

const log = console.log;

log(`exports`, exports);
log(`module`, module); // TypeError: Cannot set property 'a' of undefined
// module.export.a = 1;
// module.export.b = 2;
// module.export.c = 3;
// module.export.d = 4; // 自定义属性 umd
module.umd = {
a: 1,
b: 2,
c: 3,
d: 4,
};
// 自定义属性 export
module.export = {
a: 1,
b: 2,
c: 3,
d: 4,
}; exports.a = 11;
exports.b = 22;
exports.c = 33;
exports.d = 44; // module.exports 覆盖 exports
module.exports = { c: 333 };
module.exports.d = 444; log(`\nexports`, exports);
log(`module`, module); /* exports { a: 11, b: 22, c: 33, d: 44 }
module Module {
id: '.',
path: '/Users/xgqfrms-mbp/Documents/GitHub/umd-npm-package/src',
exports: { c: 333, d: 444 },
parent: null,
filename: '/Users/xgqfrms-mbp/Documents/GitHub/umd-npm-package/src/export.js',
loaded: false,
children: [],
paths: [
'/Users/xgqfrms-mbp/Documents/GitHub/umd-npm-package/src/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/umd-npm-package/node_modules',
'/Users/xgqfrms-mbp/Documents/GitHub/node_modules',
'/Users/xgqfrms-mbp/Documents/node_modules',
'/Users/xgqfrms-mbp/node_modules',
'/Users/node_modules',
'/node_modules'
],
umd: { a: 1, b: 2, c: 3, d: 4 },
export: { a: 1, b: 2, c: 3, d: 4 }
}
*/

https://nodejs.org/api/modules.html#modules_exports_shortcut

refs

http://www.ruanyifeng.com/blog/2015/05/require.html

Module System

https://www.cnblogs.com/xgqfrms/p/9493550.html

node --experimental-modules & node.js ES Modules

https://www.cnblogs.com/xgqfrms/p/13591967.html



xgqfrms 2012-2020

www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!


Node.js require 模块加载原理 All In One的更多相关文章

  1. Node.js中模块加载机制

    1.模块查找规则-当模块拥有路径但没有后缀时:(require(‘./find’)) require方法根据模块路径查找模块,如果是完整路径,直接引入模块: 如果模块后缀省略,先找同名JS文件,再找同 ...

  2. 第三课:sea.js模块加载原理

    模块加载,其实就是把js分成很多个模块,便于开发和维护.因此加载很多js模块的时候,需要动态的加载,以便提高用户体验. 在介绍模块加载库之前,先介绍一个方法. 动态加载js方法: function l ...

  3. AMD-require.js模块加载原理

    项目中使用大了require.js,功能实现,现重新学习下模块加载原理相关知识,借鉴如下博文:https://blog.csdn.net/ai52011/article/details/7711361 ...

  4. js 简易模块加载器 示例分析

    前端模块化 关注前端技术发展的各位亲们,肯定对模块化开发这个名词不陌生.随着前端工程越来越复杂,代码越来越多,模块化成了必不可免的趋势. 各种标准 由于javascript本身并没有制定相关标准(当然 ...

  5. Layui 源码浅读(模块加载原理)

    经典开场 // Layui ;! function (win) { var Lay = function () { this.v = '2.5.5'; }; win.layui = new Lay() ...

  6. browserify.js 的模块加载

    browserify的模块加载基本上和nodejs的类似: nodejs 的模块加载是依次去读取文件然后用一个类eval() 函数执行并返回module.exports的结果.为了避免循环加载,在加载 ...

  7. node.js的包加载机制

    加载一个模块 require('moduleName'); 现在核心模块中加载,如果核心模块中没有,那么就去node_modules目录下去找,核心模块的优先级最高. 如果加载模块式省略了文件的后缀名 ...

  8. Node.js实现热加载

    不管是node.js原生开发,还是借助express,kora等框架开发node.js的情况下,在对代码做出更新后,都是需要重启已生效我们的文件的. 本文记录一次在原生node.js开发的时候,为项目 ...

  9. Vue中结合Flask与Node.JS的异步加载功能实现文章的分页效果

    你好!欢迎阅读我的博文,你可以跳转到我的个人博客网站,会有更好的排版效果和功能. 此外,本篇博文为本人Pushy原创,如需转载请注明出处:http://blog.pushy.site/posts/15 ...

随机推荐

  1. day03 函数基本语法及特性 2. 参数与局部变量 3. 返回值 嵌套函数 4.递归 5.匿名函数 6.函数式编程介绍 7.高阶函数 8.内置函数

    本节内容 1. 函数基本语法及特性 2. 参数与局部变量 3. 返回值 嵌套函数 4.递归 5.匿名函数 6.函数式编程介绍 7.高阶函数 8.内置函数 温故知新 1. 集合 主要作用: 去重 关系测 ...

  2. 精通MySQL之索引篇,这篇注重练习!

    老刘是即将找工作的研究生,自学大数据开发,一路走来,感慨颇深,网上大数据的资料良莠不齐,于是想写一份详细的大数据开发指南.这份指南把大数据的[基础知识][框架分析][源码理解]都用自己的话描述出来,让 ...

  3. 分布式缓存 — memcache

    MemCache是一个自由.源码开放.高性能.分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站访问的速度.Mem ...

  4. Prometheus+Grafana通过kafka_exporter监控kafka

    Prometheus+Grafana通过kafka_exporter监控kafka 一.暴露 kafka-metric 方式 二.jmx_exporter方式 2.1 下载jmx_prometheus ...

  5. CyclicBarrier---可重用的循环栅栏

    很多文章只是提了下可重用,具体这个栅栏怎么可重用的,很多没有说明,这里会解开你的疑惑. CyclicBarrier是一个同步工具类,它允许一组线程互相等待,直到达到某个公共屏障点.与CountDown ...

  6. git回退版本,再返回最新分支git pull失败的解决经验

    ​ 点击"蓝字"关注我吧 作者:良知犹存 转载授权以及围观:欢迎添加微信公众号:Conscience_Remains 总述     一篇解决gti分支切换问题的文章,大家应该都有过 ...

  7. P2762 太空飞行计划问题 (最小割)

    题意:n个实验 每个实验可获利ai元 做每个实验需要几个仪器 购买每个仪器有不同的花费 不同实验可能会用到同一个仪器 只用购买一次 求最大收益 题解:......................... ...

  8. hdu4719 Oh My Holy FFF 线段树维护dp

    题意:给你一个长度为n的数组v,你需要把这个数组分成很多段,你需要保证每一段的长度不能超过k我们设一共有m段,每一段右边界那个数为bi那么我们要使得sum(bi*bi-b(i-1))最大 (1< ...

  9. 【uva 1152】4 Values Whose Sum is Zero(算法效率--中途相遇法+Hash或STL库)

    题意:给定4个N元素几个A,B,C,D,要求分别从中选取一个元素a,b,c,d使得a+b+c+d=0.问有多少种选法.(N≤4000,D≤2^28) 解法:首先我们从最直接最暴力的方法开始思考:四重循 ...

  10. UVA 10480 Sabotage (最大流) 最小割边

    题目 题意: 编写一个程序,给定一个网络规范和破坏每个连接的成本,确定要切断哪个连接,以便将首都和最大的城市分离到尽可能低的成本. 分割-------------------------------- ...