首頁  >  文章  >  web前端  >  深入了解node.js中的module-alias(分享一些避坑方法)

深入了解node.js中的module-alias(分享一些避坑方法)

青灯夜游
青灯夜游轉載
2021-12-20 11:05:495413瀏覽

這篇文章帶大家了解一下node.js中的module-alias,介紹一下module-alias原理、module-alias的一個常見問題(坑),希望對大家有幫助!

深入了解node.js中的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使用有一段說明:

深入了解node.js中的module-alias(分享一些避坑方法)

#但是當時確實也是沒有註意到這塊說明,要不然也不會踩這個坑了,下次看使用說明要仔細了,不過這麼長的使用說明,大概率還是不會看的這麼仔細。 。 。畢竟看起來這麼簡單的使用方法,好像似乎是不會出什麼問題的吧

module-alias/register流程

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

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 教程!!

以上是深入了解node.js中的module-alias(分享一些避坑方法)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除