Maison >interface Web >js tutoriel >En savoir plus sur l'alias de module dans node.js (partager quelques pièges)

En savoir plus sur l'alias de module dans node.js (partager quelques pièges)

青灯夜游
青灯夜游avant
2021-12-20 11:05:495486parcourir

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 !

En savoir plus sur l'alias de module dans node.js (partager quelques pièges)

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环境下的路径别名功能。一般前端开发可能会比较熟悉webpackalias配置、typescriptpaths配置等,这些都是提供了陆军别名的功能。路径别名在代码开发过程中是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这个库存在的目的。

module-alias介绍

从官网上看,这个库使用方法只需要两步,真的已经是极简状态了。

1、路径别名配置:module-alias支持两种路径别名配置方式

  • package.json中增加_moduleAliases属性进行配置

    "_moduleAliases": {
        "@": "./src"
    }
  • 通过提供的API接口addAliasaddAliasesaddPath,增加配置

    moduleAlias.addAliases({
      '@'  : __dirname + './src',
    });

2、在项目启动时首先导入该库:require(module-alias/register)即可,当然选择使用API方式的需要导入对应的函数进行处理

一般我们都是使用package.json中配置路径别名 + 项目入口处require(module-alias/register)来使用这个库。

module-alias原理介绍

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)
}

看似简单的背后,往往也会踩坑

module-alias踩坑

一般我们会在node项目中使用module-alias库,因为node项目一般会从typescript转换成js代码,但是往往并不会进行打包处理,因为node项目中一般也确实不需要打包,显得有些冗余。这时候就需要module-alias上场了。

但是这个项目有点不一般,我们在项目中使用了多层代码组织方式,最外层有全局的package.json, 内层包有自己的package.json, 简单说是使用了monorepo的代码组织方式,问题也就是由此而来

module-alias无法正常解析在package.json中配置的路径别名

刚开始确实没想到是多层项目组织方式的问题,官网对module-alias/register

Pour faire simple, module-alias fournit la fonction d'alias de chemin dans l'environnement

nodeEn savoir plus sur lalias de module dans node.js (partager quelques pièges). 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$/, &#39;&#39;))]
  } 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&#39; current working directory (when
    // running a package manager script, e.g. `yarn start` / `npm run start`)
    // 重点看这里!!!
    candidatePackagePaths = [nodePath.join(__dirname, &#39;../..&#39;), process.cwd()]
  }
  var npmPackage, base
  for (var i in candidatePackagePaths) {
    try {
      base = candidatePackagePaths[i]
      npmPackage = require(nodePath.join(base, &#39;package.json&#39;))
      break
    } catch (e) { // noop }
  }
  // 省略了部分内容
  var aliases = npmPackage._moduleAliases || {}
  for (var alias in aliases) {
    if (aliases[alias][0] !== &#39;/&#39;) {
      aliases[alias] = nodePath.join(base, aliases[alias])
    }
  }
  // 省略了部分内容
}
🎜Voici la configuration de tsconfig.json 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. 🎜

Introduction au module-alias

🎜Depuis le site officiel, la méthode d'utilisation de cette bibliothèque ne nécessite que deux étapes, et elle est vraiment minimaliste. 🎜🎜1. Configuration de l'alias de chemin : module-alias prend en charge deux méthodes de configuration d'alias de chemin🎜
  • 🎜Ajoutez _moduleAliases dans <code>package.json code>Configuration des propriétés🎜rrreee
  • 🎜Ajouter via les interfaces API fournies addAlias, addAliases, addPath Configurer 🎜rrreee
🎜2. Importez d'abord la bibliothèque au démarrage du projet : 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 . 🎜

Principe d'introduction du module-alias

🎜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🎜

pièges de l'alias de module

🎜Généralement, nous utiliserons 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/register流程

既然踩坑了,就有必要了解一下踩坑的原因,避免反复踩坑才好。可以详细了解下module-aliasinit方法的实现。为了节省篇幅,省略了部分细节

function init (options) {
  // 省略了部分内容
  var candidatePackagePaths
  if (options.base) {
    candidatePackagePaths = [nodePath.resolve(options.base.replace(/\/package\.json$/, &#39;&#39;))]
  } 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&#39; current working directory (when
    // running a package manager script, e.g. `yarn start` / `npm run start`)
    // 重点看这里!!!
    candidatePackagePaths = [nodePath.join(__dirname, &#39;../..&#39;), process.cwd()]
  }
  var npmPackage, base
  for (var i in candidatePackagePaths) {
    try {
      base = candidatePackagePaths[i]
      npmPackage = require(nodePath.join(base, &#39;package.json&#39;))
      break
    } catch (e) { // noop }
  }
  // 省略了部分内容
  var aliases = npmPackage._moduleAliases || {}
  for (var alias in aliases) {
    if (aliases[alias][0] !== &#39;/&#39;) {
      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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer