33

路径 alias 在 Node 中的最佳姿势

 4 years ago
source link: https://quincychen.cn/path-alias/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

周末在家闲来无事,捣鼓自己荒废很久的新博客系统的Node端时,突然发现一个以前没有太注意,但是现在越看越不爽的问题。

import config from '../../../src/config'

这个 ../../../ 我越看越觉得它不顺眼,众所周知,(有追求的)程序员是很懒的,能不多写绝不多写,能用程序解决的绝不自己动手,现在就想办法干掉它。

一般而言,正常流程是

  1. google / bing
  2. 愣着干嘛,赶紧抄啊

但是,作为一个有追求的程序员,应该做的是:

根据当前自己已有的知识体系,先想想有哪几种解决方案,然后再去查找业界的解决方案

这样,我们才能不断完善自己的知识体系,培养足够的思维能力。

熟悉 webpack 的都知道它提供了一个叫 resolve 的功能,可以解决这个问题,但是如果项目中没有使用 webpack 或者我就是不想用 webpack 怎么办?

想法

我们来想想有没有其他解决方案,假定当前项目目录结构为

├── src
|   ├── config
|   └── controller
├── node_modules
├── index.js
└── package.json

1. 全局变量

第一反应,在 Node 程序里面,我们可以直接通过

global.BASE_PATH = __dirname + '/'

之后就可以这么使用

import config from BASE_PATH + 'src/config'

emmmm,太丑了,而且也没方便到哪去,抬走

2. symlink

再一想,想起来在系统中有一个符号链接的东西,再利用 Node 的模块加载机制,我们可以在项目的 node_modules 下建一个链接到项目目录的符号链接。

  1. Linux / MacOS (其实就是 unix 内核的系统)

    ln -nsf node_modules src
  2. Windows

    mklink \D src node_modules

然后就可以这么使用

import config from 'src/config'

看起来不错,但是也有一些槽点

  • 不同平台创建命令不一致
  • 每个clone仓库的人都要手动添加符号链接

既然有槽点,那就解决一下,印象中 Node 的 fs 模块提供了创建 symlink 的方法: fs.symlinkSync ,我们把具体代码写出来,第一个问题迎刃而解。

const src='../src'
const dir='node_modules/src'
const fs = require('fs')
fs.exists(dir, function (e) { e || fs.symlinkSync(src, dir, 'dir') })

第二个问题就简单了,我们利用 package.json scripts 中的 postinstall 钩子,它会在 npm run install 之前被执行,我们把前面写好的代码浓缩一下放进去用 node 执行即可。

{
  "scripts": {
    "postinstall": "node -e \"var src='../src',dir='node_modules/src',fs=require('fs');fs.exists(dir,function(e){e||fs.symlinkSync(src,dir,'dir')});\""
    }
}

到这里看起来就很不错了,不过仔细一想,还有个小问题。当我们想再加一个 alias,比如 @controller -> src/controller 的时候,喔霍,又得改 postinstall 脚本。

更好一点的做法是将 postinstall 中的脚本抽出来保存成一个单独的文件,但是还是无法避免新增时要修改脚本的问题。

我们可以再通过命令行参数来解决这个问题,但是需要引入新的命令,整个流程就比较繁琐了,新人接手可能还要花点时间才能搞懂这里的逻辑。

3. 环境变量

想了想,又想起来一个知识点,我们可以通过设置环境变量 NODE_PATH 指向对应的路径

export NODE_PATH='项目路径'

之后就可以直接使用

import config from 'src/config'

原理和Node模块加载机制有关,感兴趣的自行查阅,这里就不多说了。

这个方案的缺点也很明显,没错, NODE_PATH 只有一个,因此生成的 alias 只有该路径下的最顶层,没办法添加 src/controller -> controller 这样的 alias。

业界方案

自己的思考大概结束了,我们来看看业界的实现方案。搜了一圈之后,聚焦在一个轮子上: module-alias

看了下源码是通过 hack 原生模块 module 的方法实现的,Node在引入模块时其实就是调用的 module 模块。具体源码就不多说了,我们直接来看最佳实践(我认为的)

module-alias 提供了两种 alias 添加模式

  1. 配置package.json

    通过配置 _moduleAliases 字段添加 alias

    // package.json
    {
      "_moduleAliases": {
        "@src": "src",
        "@config": "src/config"
      }
    }
    // index.js
    import 'module-alias/register'
  2. 在项目代码中添加

    import { addAlias } from 'module-alias'
    
    addAlias('@root', __dirname)
    addAliases({
     '@src': __dirname + 'src',
     '@config': __dirname + 'src/config'
    })

之后就可以使用配置的 alias 进行开发了,但是还有一个问题,我们在 VSCode 中的引用没办法点击跳转,引用的模块提示也不能正常加载。

这是因为 VSCode 并不知道这个路径解析的配置,所以需要额外添加配置。只需要配置一下 jsconfig.json (使用 typescript 的修改 tsconfig.json )的 compilerOptions

{
  "compilerOptions": {
    "moduleResolution": "node",
    "baseUrl": ".",
    "rootDir": ".",
    "paths": {
      "@config": ["src/config"],
      "@src/*": ["src/*"]
    }
  }
}

需要注意的是,jsconfig.json更改后需要重新打开 VSCode 配置才会生效

以上配置完即可愉快的进行开发了,提高效率、治好强迫症的同时代码提示、引用跳转等都可以正常使用。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK