Heim >Web-Frontend >js-Tutorial >Erfahren Sie mehr über Modulalias in node.js (teilen Sie einige Fallstricke)
Dieser Artikel führt Sie zum Verständnis des Modulalias in node.js, stellt das Prinzip des Modulalias vor und zeigt ein häufiges Problem (Pit) des Modulalias auf. Ich hoffe, dass es für alle hilfreich ist!
Zunächst muss vorgestellt werden, was module-alias
ist. Hier ist der offizielle Website-Link (offizielle Website-Adresse https://github.com/ilearnio/module-alias). ). module-alias
是什么,这里有其官网链接(官网地址 https://github.com/ilearnio/module-alias)。
简单点说,module-alias
提供了在node
环境下的路径别名功能。一般前端开发可能会比较熟悉webpack
的alias
配置、typescript
的paths
配置等,这些都是提供了陆军别名的功能。路径别名在代码开发过程中是yyds,不然你看到这种../../../../xx
路径时,是肯定会抓狂的。
使用webpack
打包的项目webpack
本身会处理源代码中路径别名的配置到打包后代码的转换过程,但是如果单纯使用typescript
进行编译的项目,虽然typescript
在编译过程中可以正常处理paths
中路径别名的配置,但是并不会改变打包后的代码,造成在打包后的代码中仍然存在路径别名配置,看一个经过typescript
编译后的代码:
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); require("./module-alias-register"); var commands_1 = require("@/commands"); var package_json_1 = require("../package.json"); (0, commands_1.run)(package_json_1.version);
这里是tsconfig.json
的配置内容
"paths": { "@/*": [ "src/*" ] }
可以看到在经过typescript
编译后的代码中,仍然存在@符号,然而当代码运行的过程中,比如允许在node
中,require
并不能正常识别路径里的这个符号,导致找不到相应模块而抛出异常。
这也是module-alias
这个库存在的目的。
从官网上看,这个库使用方法只需要两步,真的已经是极简状态了。
1、路径别名配置:module-alias
支持两种路径别名配置方式
在package.json
中增加_moduleAliases
属性进行配置
"_moduleAliases": { "@": "./src" }
通过提供的API接口addAlias
、addAliases
、addPath
,增加配置
moduleAlias.addAliases({ '@' : __dirname + './src', });
2、在项目启动时首先导入该库:require(module-alias/register)
即可,当然选择使用API方式的需要导入对应的函数进行处理
一般我们都是使用package.json
中配置路径别名 + 项目入口处require(module-alias/register)
来使用这个库。
module-alias
通过覆写了全局对象Module
上的方法_resolveFilename
来实现路径别名的转换,简单来说就是通过拦截原生的_resolveFilename
方法调用,进行路径别名的转换,当获取到文件的真实路径后,再调用原声的_resolveFilename
方法。
下面是其源代码,基本上分为两部分:路径别名转换+原生_resolveFilename
调用
var oldResolveFilename = Module._resolveFilename Module._resolveFilename = function (request, parentModule, isMain, options) { for (var i = moduleAliasNames.length; i-- > 0;) { var alias = moduleAliasNames[i] if (isPathMatchesAlias(request, alias)) { var aliasTarget = moduleAliases[alias] // Custom function handler if (typeof moduleAliases[alias] === 'function') { var fromPath = parentModule.filename aliasTarget = moduleAliases[alias](fromPath, request, alias) if (!aliasTarget || typeof aliasTarget !== 'string') { throw new Error('[module-alias] Expecting custom handler function to return path.') } } request = nodePath.join(aliasTarget, request.substr(alias.length)) // Only use the first match break } } return oldResolveFilename.call(this, request, parentModule, isMain, options) }
看似简单的背后,往往也会踩坑
一般我们会在node
项目中使用module-alias
库,因为node
项目一般会从typescript
转换成js
代码,但是往往并不会进行打包处理,因为node
项目中一般也确实不需要打包,显得有些冗余。这时候就需要module-alias
上场了。
但是这个项目有点不一般,我们在项目中使用了多层代码组织方式,最外层有全局的package.json
, 内层包有自己的package.json
, 简单说是使用了monorepo
的代码组织方式,问题也就是由此而来。
module-alias无法正常解析在package.json中配置的路径别名
刚开始确实没想到是多层项目组织方式的问题,官网对module-alias/register
module-alias
stellt die Pfad-Alias-Funktion in der node
-Umgebung bereit. Allgemeine Frontend-Entwickler sind möglicherweise mit der alias
-Konfiguration von webpack
, der paths
-Konfiguration von typescript
usw. vertraut . Diese werden bereitgestellt. Die Funktion des Armee-Alias wurde hinzugefügt. Der Pfad-Alias ist im Code-Entwicklungsprozess yyds, sonst werden Sie definitiv verrückt, wenn Sie diesen ../../../../xx
-Pfad sehen.
Mit webpack
gepackte Projekte übernimmt webpack
selbst den Konvertierungsprozess von der Konfiguration des Pfadalias im Quellcode zum gepackten Code, aber wenn Sie einfach typescript Bei durch Code kompilierten Projekten kann <code>typescript
zwar normalerweise die Konfiguration von Pfadaliasen in paths
während des Kompilierungsprozesses verarbeiten, dies wird jedoch nicht geändert Daher ist die Pfad-Alias-Konfiguration weiterhin im gepackten Code vorhanden. Schauen Sie sich einen von typescript
kompilierten Code an:
function init (options) { // 省略了部分内容 var candidatePackagePaths if (options.base) { candidatePackagePaths = [nodePath.resolve(options.base.replace(/\/package\.json$/, ''))] } else { // There is probably 99% chance that the project root directory in located // above the node_modules directory, // Or that package.json is in the node process' current working directory (when // running a package manager script, e.g. `yarn start` / `npm run start`) // 重点看这里!!! candidatePackagePaths = [nodePath.join(__dirname, '../..'), process.cwd()] } var npmPackage, base for (var i in candidatePackagePaths) { try { base = candidatePackagePaths[i] npmPackage = require(nodePath.join(base, 'package.json')) break } catch (e) { // noop } } // 省略了部分内容 var aliases = npmPackage._moduleAliases || {} for (var alias in aliases) { if (aliases[alias][0] !== '/') { aliases[alias] = nodePath.join(base, aliases[alias]) } } // 省略了部分内容 }🎜Hier ist die Konfiguration von
tsconfig.json
Inhalt🎜rrreee🎜Sie können sehen, dass das @-Symbol immer noch im von typescript
kompilierten Code vorhanden ist. Wenn der Code jedoch beispielsweise in node
ausgeführt wird, require kann dieses Symbol im Pfad nicht richtig erkennen, was dazu führt, dass das entsprechende Modul gefunden und eine Ausnahme ausgelöst wird. 🎜🎜Dies ist auch der Zweck der module-alias
-Bibliothek. 🎜module-alias
unterstützt zwei Pfad-Alias-Konfigurationsmethoden🎜_moduleAliases in <code>package.json
hinzu code>Eigenschaftskonfiguration🎜rrreee
addAlias
, addAliases
, addPath
hinzu. Konfigurieren Sie 🎜rrreeerequire(module-alias/register)
Wenn Sie sich für die Verwendung der API-Methode entscheiden, müssen Sie dies natürlich tun Importieren Sie die entsprechende Funktion zur Verarbeitung🎜🎜Im Allgemeinen verwenden wir diese Bibliothek, indem wir den Pfadalias in package.json
+ require(module-alias/register)
am Projekteingang konfigurieren . 🎜module-alias
überschreibt das globale Objekt Module Die Methode <code>_resolveFilename
wird zum Konvertieren des Pfadalias verwendet, indem der native Methodenaufruf _resolveFilename
abgefangen wird Um den tatsächlichen Pfad der Datei zu ermitteln, rufen Sie die ursprüngliche Methode _resolveFilename
auf. 🎜🎜Das Folgende ist der Quellcode, der im Wesentlichen in zwei Teile unterteilt ist: Pfad-Alias-Konvertierung + nativer _resolveFilename
-Aufruf🎜rrreee🎜Hinter dem scheinbar Einfachen stecken oft Fallstricke🎜
module- im <code>node
-Projektalias -Bibliothek, da node
-Projekte im Allgemeinen von typescript
in js
-Code konvertiert werden, häufig jedoch nicht gepackt werden, weil node-Projekte müssen im Allgemeinen nicht gepackt werden, was etwas überflüssig erscheint. Hier kommt <code>module-alias
ins Spiel. 🎜🎜Aber dieses Projekt ist etwas ungewöhnlich. Wir verwenden im Projekt eine mehrschichtige Code-Organisationsmethode. Die äußerste Schicht hat ein globales package.json
und das innere Paket hat einen eigenen package.json
, vereinfacht gesagt, wird die Code-Organisationsmethode von monorepo
verwendet, daher kommt das Problem. 🎜🎜module-alias kann den in package.json konfigurierten Pfadalias nicht richtig auflösen🎜🎜Zuerst hatte ich nicht erwartet, dass es ein Problem mit der Art und Weise sein würde, wie mehrschichtige Projekte organisiert sind. Die offizielle Website unterstützt
module-alias/register
nicht. Es gibt eine Gebrauchsanweisung: 🎜🎜🎜🎜🎜Aber ich habe diese Anleitung damals wirklich nicht beachtet, sonst wäre ich nicht darauf getreten Diese Falle sollten Sie das nächste Mal sorgfältig lesen, aber es ist eine so lange Anleitung, dass Sie sie höchstwahrscheinlich nicht so genau betrachten werden. . . Es scheint doch, dass es mit einer so einfachen Anwendungsmethode keine Probleme geben wird, oder?既然踩坑了,就有必要了解一下踩坑的原因,避免反复踩坑才好。可以详细了解下module-alias
中init
方法的实现。为了节省篇幅,省略了部分细节
function init (options) { // 省略了部分内容 var candidatePackagePaths if (options.base) { candidatePackagePaths = [nodePath.resolve(options.base.replace(/\/package\.json$/, ''))] } else { // There is probably 99% chance that the project root directory in located // above the node_modules directory, // Or that package.json is in the node process' current working directory (when // running a package manager script, e.g. `yarn start` / `npm run start`) // 重点看这里!!! candidatePackagePaths = [nodePath.join(__dirname, '../..'), process.cwd()] } var npmPackage, base for (var i in candidatePackagePaths) { try { base = candidatePackagePaths[i] npmPackage = require(nodePath.join(base, 'package.json')) break } catch (e) { // noop } } // 省略了部分内容 var aliases = npmPackage._moduleAliases || {} for (var alias in aliases) { if (aliases[alias][0] !== '/') { aliases[alias] = nodePath.join(base, aliases[alias]) } } // 省略了部分内容 }
可以看重点部分,如果我们没有给base参数,module-alias
默认会从../../
目录和当前目录下找寻package.json
文件,而且../..
目录下的package.json
文件的优先级比当前目录下的优先级还要高,这里的优先级设置似乎和正常的优先级逻辑有点差别,一般都会让当前目录的优先级比较高才比较符合正常逻辑,所以会导致加载的不是当前目录下的package.json
文件,而导致找不到路径别名配置而出错。
关于这点似乎有不少人踩坑了,还有人提了issues,但是似乎暂时并没有人回应。
通过API方式注册路径别名,或者手动调用init
方法,传入base参数,指定package.json
文件.
似乎只有踩坑了,才会更深入的了解
更多node相关知识,请访问:nodejs 教程!!
Das obige ist der detaillierte Inhalt vonErfahren Sie mehr über Modulalias in node.js (teilen Sie einige Fallstricke). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!