Cet article vous amènera à comprendre l'alias de module dans node.js, à présenter le principe de l'alias de module et un problème courant (fosse) de l'alias de module. J'espère qu'il sera utile à tout le monde !
Tout d'abord, il faut présenter ce qu'est module-alias
Voici le lien de son site officiel (adresse officielle du site 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
fournit la fonction d'alias de chemin dans l'environnement node
. Les développeurs front-end généraux peuvent être familiers avec la configuration alias
de webpack
, la configuration paths
de typescript
, etc. . Ceux-ci sont fournis. La fonction d'alias d'armée a été ajoutée. L'alias du chemin est yyds dans le processus de développement du code, sinon vous deviendrez certainement fou lorsque vous verrez ce chemin ../../../../xx
.
Les projets packagés à l'aide de webpack
webpack
lui-même géreront le processus de conversion depuis la configuration de l'alias de chemin dans le code source vers le code packagé, mais si vous utilisez simplement typescript</ Pour les projets compilés par code>, bien que <code>typescript
puisse normalement gérer la configuration des alias de chemin dans paths
pendant le processus de compilation, il ne changera pas le Par conséquent, la configuration de l'alias de chemin existe toujours dans le code empaqueté. Regardez un code compilé par typescript
:
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]) } } // 省略了部分内容 }
tsconfig.json</code. > Contenu🎜rrreee🎜Vous pouvez voir que le symbole @ existe toujours dans le code compilé par <code>typescript
. Cependant, lorsque le code est exécuté, par exemple, dans node
, <. code>require ne peut pas reconnaître correctement ce symbole dans le chemin, ce qui entraîne la recherche du module correspondant et la levée d'une exception. 🎜🎜C'est aussi le but de la bibliothèque module-alias
. 🎜module-alias
prend en charge deux méthodes de configuration d'alias de chemin🎜_moduleAliases dans <code>package.json
code>Configuration des propriétés🎜rrreeeaddAlias
, addAliases
, addPath
Configurer 🎜rrreee< /li>require(module-alias/register)
Bien sûr, si vous choisissez d'utiliser la méthode API, vous devez le faire. importer la fonction correspondante pour le traitement🎜🎜Généralement, nous utilisons cette bibliothèque en configurant l'alias de chemin dans package.json
+ require(module-alias/register)
à l'entrée du projet . 🎜module-alias
écrase l'objet global Module La méthode <code>_resolveFilename
sur est utilisé pour convertir l'alias de chemin. En termes simples, il s'agit de convertir l'alias de chemin en interceptant l'appel de méthode natif _resolveFilename
lors de la spécification. le chemin réel du fichier, appelez la méthode _resolveFilename
d'origine. 🎜🎜Voici son code source, qui est essentiellement divisé en deux parties : conversion d'alias de chemin + appel natif _resolveFilename
🎜rrreee🎜Derrière ce qui semble simple, il y a souvent des pièges🎜
module- dans l'alias du projet <code>node
, car les projets node
sont généralement convertis du code typescript
en code js
, mais ils ne sont souvent pas empaquetés car node Les projets
n'ont généralement pas besoin d'être empaquetés, ce qui semble un peu redondant. C'est à ce moment-là que module-alias
entre en jeu. 🎜🎜Mais ce projet est un peu inhabituel. Nous utilisons une méthode d'organisation de code multicouche dans le projet. La couche la plus externe a un package.json
global, et le package interne a son propre . package.json
, en termes simples, la méthode d'organisation du code de monorepo
est utilisée, c'est de là que vient le problème. 🎜🎜module-alias ne peut pas résoudre correctement l'alias de chemin configuré dans package.json🎜🎜Au début, je ne m'attendais pas à ce que ce soit un problème avec la façon dont les projets multicouches sont organisés. ne supporte pas
module-alias/register
Il y a une notice d'utilisation : 🎜🎜🎜🎜🎜Mais je n'ai vraiment pas fait attention à cette instruction à l'époque, sinon je ne serais pas intervenu ce piège. Vous devriez lire attentivement les instructions la prochaine fois, mais c'est une instruction si longue que vous ne la regarderez probablement pas aussi attentivement. . . Après tout, il semble qu'il n'y aura aucun problème avec une méthode d'utilisation aussi simple, n'est-ce pas ?既然踩坑了,就有必要了解一下踩坑的原因,避免反复踩坑才好。可以详细了解下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 教程!!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!