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. 【转载】HTTP 协议详细介绍

    背景 当你在浏览器地址栏敲入"http://www.cnblogs.com/",然后猛按回车,呈现在你面前的,将是博客园的首页了(这真是废话,你会认为这是理所当然的).作为一个开发 ...

  2. Java并发练习

    1.按顺序打印ABC 三个线程,每个线程分别打印A,B,C各十次,现在要求按顺序输出A,B,C package concurrency; import java.util.concurrent.Exe ...

  3. 使用 html5 svg 绘制图形

    有一次看一个项目的时候,看到图片的格式为svg,作为萌新的我瞬间有点小懵,这可是之前从没有见到过的格式,于是就开始上某度进行学习,发现某博主的优秀文章,进行转载方便自己学习,感谢原博主的优秀文章. · ...

  4. Vue.js到前端工程化

    b站视频地址:黑马程序员Vue.js到前端工程化(webpack打包,以及Vue-cli3和Element-UI的使用) vue学习系列 1.vue概述 2.vue基本使用 3.vue模板语法 4.指 ...

  5. Angular写一个Form组件-TagInput

    前端开发少不了和表单打交道; Angular中, 提供了强大的表单的支持, 响应式表单(Reactive Form) 和 模板驱动的表单(Template-driven Form) 的双向数据流给我们 ...

  6. redis-避免生产环境使用keys命令

    redis作为内存数据库, 有着很高的性能, Redis能读的速度是110000次/s, 写的速度是81000次/s; 除了进行持久化操作时, redis采用的是单线程架构, 所以如果我们在开发中不恰 ...

  7. vim快捷键收藏版

    总述 附加一篇介绍文哈,关于vim快捷键的介绍.vim和vscode 到底谁更好用,大家争得不可开交,然后我就在vscode里面装了一个vim插件,完美得解决了这个问题,用完之后觉得真香,所以我就整理 ...

  8. 2019 China Collegiate Programming Contest Qinhuangdao Onsite F. Forest Program(DFS计算图中所有环的长度)

    题目链接:https://codeforces.com/gym/102361/problem/F 题意 有 \(n\) 个点和 \(m\) 条边,每条边属于 \(0\) 或 \(1\) 个环,问去掉一 ...

  9. Codeforces Round #628 (Div. 2) B. CopyCopyCopyCopyCopy(Set)

    题意: 给你一个数组,可以像题目那样无限拼接,问递增子序列的最大长度(可不连续). 思路: 序列的最大长度即为数组中不同元素的个数. Tips: 一开始不知道back-to-back什么意思,看到题目 ...

  10. Gym 101485 E Elementary Math 网络流 或者 二分图

    题意: 输入一个n,后面输入n行,每一行两个数a.b.你可以对a.b进行三种操作:+.-.* 你需要保证对每一行a.b选取一个操作得到一个结果 你要保证这n行每一个式子选取的操作之后得到的结果都不一样 ...