node.js require() code source interprétation_node.js
En 2009, le projet Node.js est né, et tous les modules sont au format CommonJS.
Aujourd'hui, l'entrepôt de modules Node.js npmjs.com a stocké 150 000 modules, dont la plupart sont au format CommonJS.
Le cœur de ce format est l'instruction require, à travers laquelle les modules sont chargés. Lors de l'apprentissage de Node.js, vous devez apprendre à utiliser l'instruction require. Grâce à l'analyse du code source, cet article présente en détail le mécanisme de fonctionnement interne de l'instruction require pour vous aider à comprendre le mécanisme du module de Node.js.
1. Utilisation de base de require()
Avant d'analyser le code source, introduisons d'abord la logique interne de l'instruction require. Si vous voulez simplement savoir comment utiliser require, lisez simplement ce paragraphe.
Le contenu suivant est traduit du "Manuel d'utilisation du nœud" .
Lorsque Node rencontre require(X), il est traité dans l'ordre suivant.
(1) Si X est un module intégré (tel que require('http'))
a. Revenez au module.
b. Aucune autre exécution.
(2) Si X commence par "./" ou "/" ou "../"
a. Déterminez le chemin absolu de X en fonction du module parent où se trouve X.
b. Traiter
X
X.js
X.json
X.node
c. Traiter
X/package.json (champ principal)
X/index.js
X/index.json
X/index.node
(3) Si X n'a pas de chemin
a. Déterminez le répertoire d'installation possible de X en fonction du module parent où se trouve X.
b. Dans chaque répertoire tour à tour, chargez X comme nom de fichier ou nom de répertoire.
(4) Lancez "introuvable"
Veuillez voir un exemple.
Le fichier de script actuel /home/ry/projects/foo.js exécute require('bar') , qui appartient à la troisième situation ci-dessus. Le processus de fonctionnement interne de Node est le suivant.
Tout d'abord, déterminez que le chemin absolu de x peut être les emplacements suivants et recherchez chaque répertoire tour à tour.
/home/ry/projects/node_modules/bar
/home/ry/node_modules/bar
/home/node_modules/bar
/node_modules/bar
Lors de la recherche, Node traite d'abord bar comme nom de fichier, essaie de charger les fichiers suivants dans l'ordre et renvoie tant que l'on réussit.
bar bar.js bar.json bar.node
Si aucun des deux ne réussit, cela signifie que bar peut être un nom de répertoire, alors essayez de charger les fichiers suivants dans l'ordre.
bar/package.json (champ principal)
bar/index.js
bar/index.json
bar/index.node
Si le fichier ou le répertoire correspondant à bar ne peut être trouvé dans aucun répertoire, une erreur sera générée.
2. Constructeur de modules
Après avoir compris la logique interne, regardons le code source.
require se trouve dans le fichier lib/module.js de Node. Pour faciliter la compréhension, le code source cité dans cet article a été simplifié et les commentaires de l'auteur original ont été supprimés.
function Module(id, parent) { this.id = id; this.exports = {}; this.parent = parent; this.filename = null; this.loaded = false; this.children = []; } module.exports = Module; var module = new Module(filename, parent);
Dans le code ci-dessus, Node définit un constructeur Module, et tous les modules sont des instances de Module. Comme vous pouvez le voir, le module actuel (module.js) est également une instance de Module.
Chaque instance a ses propres propriétés. Prenons un exemple pour voir quelles sont les valeurs de ces attributs. Créez un nouveau fichier de script a.js.
// a.js console.log('module.id: ', module.id); console.log('module.exports: ', module.exports); console.log('module.parent: ', module.parent); console.log('module.filename: ', module.filename); console.log('module.loaded: ', module.loaded); console.log('module.children: ', module.children); console.log('module.paths: ', module.paths);
Exécutez ce script.
$ node a.js module.id: . module.exports: {} module.parent: null module.filename: /home/ruanyf/tmp/a.js module.loaded: false module.children: [] module.paths: [ '/home/ruanyf/tmp/node_modules', '/home/ruanyf/node_modules', '/home/node_modules', '/node_modules' ]
可以看到,如果没有父模块,直接调用当前模块,parent 属性就是 null,id 属性就是一个点。filename 属性是模块的绝对路径,path 属性是一个数组,包含了模块可能的位置。另外,输出这些内容时,模块还没有全部加载,所以 loaded 属性为 false 。
新建另一个脚本文件 b.js,让其调用 a.js 。
// b.js var a = require('./a.js');
运行 b.js 。
$ node b.js module.id: /home/ruanyf/tmp/a.js module.exports: {} module.parent: { object } module.filename: /home/ruanyf/tmp/a.js module.loaded: false module.children: [] module.paths: [ '/home/ruanyf/tmp/node_modules', '/home/ruanyf/node_modules', '/home/node_modules', '/node_modules' ]
上面代码中,由于 a.js 被 b.js 调用,所以 parent 属性指向 b.js 模块,id 属性和 filename 属性一致,都是模块的绝对路径。
三、模块实例的 require 方法
每个模块实例都有一个 require 方法。
Module.prototype.require = function(path) { return Module._load(path, this); };
由此可知,require 并不是全局性命令,而是每个模块提供的一个内部方法,也就是说,只有在模块内部才能使用 require 命令(唯一的例外是 REPL 环境)。另外,require 其实内部调用 Module._load 方法。
下面来看 Module._load 的源码。
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._cache[filename] = module; // 第四步:加载模块 try { module.load(filename); hadException = false; } finally { if (hadException) { delete Module._cache[filename]; } } // 第五步:输出模块的exports属性 return module.exports; };
上面代码中,首先解析出模块的绝对路径(filename),以它作为模块的识别符。然后,如果模块已经在缓存中,就从缓存取出;如果不在缓存中,就加载模块。
因此,Module._load 的关键步骤是两个。
◾Module._resolveFilename() :确定模块的绝对路径
◾module.load():加载模块
四、模块的绝对路径
下面是 Module._resolveFilename 方法的源码。
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.resolveFilename 方法内部,又调用了两个方法 Module.resolveLookupPaths() 和 Module._findPath() ,前者用来列出可能的路径,后者用来确认哪一个路径为真。
为了简洁起见,这里只给出 Module._resolveLookupPaths() 的运行结果。
[ '/home/ruanyf/tmp/node_modules',
'/home/ruanyf/node_modules',
'/home/node_modules',
'/node_modules'
'/home/ruanyf/.node_modules',
'/home/ruanyf/.node_libraries',
'$Prefix/lib/node' ]
上面的数组,就是模块所有可能的路径。基本上是,从当前路径开始一级级向上寻找 node_modules 子目录。最后那三个路径,主要是为了历史原因保持兼容,实际上已经很少用了。
有了可能的路径以后,下面就是 Module._findPath() 的源码,用来确定到底哪一个是正确路径。
Module._findPath = function(request, paths) { // 列出所有可能的后缀名:.js,.json, .node var exts = 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, exts); } } // 第五步:目录中是否存在 package.json if (!filename) { filename = tryPackage(basePath, exts); } if (!filename) { // 第六步:是否存在目录名 + index + 后缀名 filename = tryExtensions(path.resolve(basePath, 'index'), exts); } // 第七步:将找到的文件路径存入返回缓存,然后返回 if (filename) { Module._pathCache[cacheKey] = filename; return filename; } } // 第八步:没有找到文件,返回false return false; };
经过上面代码,就可以找到模块的绝对路径了。
有时在项目代码中,需要调用模块的绝对路径,那么除了 module.filename ,Node 还提供一个 require.resolve 方法,供外部调用,用于从模块名取到绝对路径。
require.resolve = function(request) { return Module._resolveFilename(request, self); }; // 用法 require.resolve('a.js') // 返回 /home/ruanyf/tmp/a.js
五、加载模块
有了模块的绝对路径,就可以加载该模块了。下面是 module.load 方法的源码。
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; };
上面代码中,首先确定模块的后缀名,不同的后缀名对应不同的加载方法。下面是 .js 和 .json 后缀名对应的处理方法。
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; } };
这里只讨论 js 文件的加载。首先,将模块文件读取成字符串,然后剥离 utf8 编码特有的BOM文件头,最后编译该模块。
module._compile 方法用于模块的编译。
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) { // 模块源码 });
也就是说,模块的加载实质上就是,注入exports、require、module三个全局变量,然后执行模块的源码,然后将模块的 exports 变量的值输出。
(完)

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Utilisation de require : 1. Introduire des modules : Dans de nombreux langages de programmation, require est utilisé pour introduire des modules ou des bibliothèques externes afin que les fonctions qu'ils fournissent puissent être utilisées dans le programme. Par exemple, dans Ruby, vous pouvez utiliser require pour charger des bibliothèques ou des modules tiers ; 2. Importer des classes ou des méthodes : dans certains langages de programmation, require est utilisé pour importer des classes ou des méthodes spécifiques afin qu'elles puissent être utilisées dans le fichier actuel. ; 3. Effectuer des tâches spécifiques : dans certains langages ou frameworks de programmation, require est utilisé pour effectuer des tâches ou des fonctions spécifiques.

Dans le développement PHP, nous rencontrons souvent de telles invites d'erreur : fatalerror:require(): Failedopeningrequired'data/tdk.php'. Cette erreur est généralement liée au traitement des fichiers dans les applications PHP. Les raisons spécifiques peuvent être des chemins de fichiers incorrects, des fichiers inexistants ou des autorisations de fichiers insuffisantes. Dans cet article, nous vous présenterons quelques astuces pour résoudre ces messages d'erreur. Vérifiez le chemin du fichier si "fatal

Méthode de réparation d'erreur Fatalerror:require():Failedopeningrequired'data/tdk.php Au cours du processus de développement ou de maintenance d'un site Web, nous rencontrons souvent diverses erreurs. L'une des erreurs courantes est "Fatalerror:require():Failedopeningrequired'data/tdk.php'". Cette erreur

Présentation des méthodes pour résoudre l'erreur FatalError dans l'en-tête PHP : require():Failedopeningrequired'data/tdk.php'(include_path='.;C:phppear') : lors de l'utilisation de PHP pour développer des sites Web, vous rencontrez souvent divers problèmes. Une telle erreur. Parmi eux, "FatalError:require():Failedopeningrequ

React peut utiliser require, et la bonne façon de l'utiliser est : 1. Lisez l'image via "<img src={require('../img/icon1.png')} alt="" />" 2. Utilisez la méthode "require('~/images/2.png').default" pour lire l'image ; 3. Divisez le champ img en deux parties : le nom du fichier et le nom de l'image, puis utilisez "require('@/assets) " méthode pour le lire. Prenez-le.

require in node est une fonction qui accepte un paramètre, le paramètre formel est nommé id et le type est String ; la fonction require peut importer des modules, des fichiers JSON et des fichiers locaux. Le module est accessible via un chemin relatif à partir de "node_modules ; ", "Module local" ou "Fichier JSON", le chemin sera celui de la variable "__dirname" ou du répertoire de travail actuel.

Comment résoudre l'erreur FatalError:require():Failedopeningrequired'data/tdk.php' dans les en-têtes PHP associés Lors du développement d'applications PHP, nous pouvons rencontrer diverses erreurs. L'une des erreurs courantes est "FatalError:require():Failedopeningrequired'data/tdk.php'". C'est faux

Le modèle d'usine est largement utilisé dans le développement de logiciels et constitue un modèle de conception pour la création d'objets. Java est un langage de programmation populaire largement utilisé dans l’industrie. En Java, il existe de nombreuses implémentations différentes du modèle d'usine. Dans cet article, nous interpréterons le modèle d'usine Java du point de vue du code source et explorerons trois méthodes d'implémentation différentes. Le modèle d'usine de Java peut nous aider à créer et à gérer des objets. Il centralise le processus d'instanciation des objets dans une classe d'usine, réduisant ainsi le couplage entre les classes et améliorant
