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. C# 防止程序多开(重复开启)

    Mutex(mutual exclusion,互斥)是 .Net Framework 中提供跨多个线程同步访问的一个类.它非常类似了 Monitor 类,因为他们都只有一个线程能拥有锁定.而操作系统能 ...

  2. 【LinuxShell】free 命令详解

    前言 free命令用来显示Linux中的内存使用信息,包括空闲的.已用的物理内存,swap内存,及被内核使用的buffer.在Linux系统监控的工具中,free命令是最经常使用的命令之一. 命令格式 ...

  3. TRUNK与VTP

    TRUNK协议: 交换机之间VLAN通信: 同一个VLAN可以跨越多个交换机 主干功能支持多个VLAN的数据 Trunk(主干) VLAN 中交换机之间的链路:用来承载多个VLAN的数据流. Trun ...

  4. 在nodejs中创建cluster

    目录 简介 cluster集群 cluster详解 cluster中的event cluster中的方法 cluster中的属性 cluster中的worker 总结 在nodejs中创建cluste ...

  5. log4j 动态配置,重启项目配置失效问题

    公司项目升级之后,成功去掉了log4j.properties配置文件,实现页面动态配置日志级别. 很经典的两个配置,但是最终还是随着时代的进步而被优化,最终弄成了可配置项 但是随之问题就来了,当我启动 ...

  6. MySQL索引的原理,B+树、聚集索引和二级索引

    MySQL索引的原理,B+树.聚集索引和二级索引的结构分析 一.索引类型 1.1 B树 1.2 B+树 1.3 哈希索引 1.4 聚集索引(clusterd index) 1.5 二级索引(secon ...

  7. linux git 命了

    #拉取远程分支代码到本地git clone -b 分支名称 sshGit路径 #更新远程代码到本地git pull #提交本地修改的代码到本地仓库git commit -m "自动打包&qu ...

  8. 原生js拖拽功能制作滑动条实例教程

    拖拽属于前端常见的功能,很多效果都会用到js的拖拽功能.滑动条的核心功能也就是使用js拖拽滑块来修改位置.一个完整的滑动条包括 滑动条.滑动痕迹.滑块.文本 等元素,先把html代码写出来,如下所示: ...

  9. Hyperbase常用SQL

    1.创建表 1.1 建HBase内表 CREATE TABLE hbase_inner_table(   key1 string,   bi bigint,   dc decimal(10,2),   ...

  10. LA 3641 Leonardo的笔记本 & UVA 11077 排列统计

    LA 3641 Leonardo的笔记本 题目 给出26个大写字母的置换B,问是否存在要给置换A,使得 \(A^2 = B\) 分析 将A分解为几个循环,可以观察经过乘积运算得到\(A^2\)后,循环 ...