|
| 1 | +这是一篇水文,讲讲踩坑的经历。 |
| 2 | + |
| 3 | +# 背景 |
| 4 | + |
| 5 | +起因是在写 Node 时,受够了 `require ('../../../../helper.js')` 这种相对路径。不够直观不谈,如果将来在别的地方用,都不能直接 copy 过来,还得重新计算相对路径,因此希望用绝对路径(换句话说就是永远相对根路径)来表示。 |
| 6 | + |
| 7 | +一种比较简单的方案是封装 `require` 函数: |
| 8 | + |
| 9 | +```js |
| 10 | +global.rootRequire = function(name) { |
| 11 | + return require(__dirname + '/' + name); |
| 12 | +} |
| 13 | +``` |
| 14 | + |
| 15 | +在我们的 `rootRequire` 函数中,所有的路径都会被加上 `__dirname` 的前缀,也就实现了绝对路径。 |
| 16 | + |
| 17 | +这么做功能上没有问题,然而似乎 VSCode 对这种写法支持得不够好(有了解的大佬还望指教),表现为以下两个问题: |
| 18 | + |
| 19 | +1. 虽然我们把 `rootRequire` 定义为全局的,但在别的文件中输入这个单词时,并没有自动补全 |
| 20 | +2. 通过这种方式引入的模块,不能跳转到模块的实现,也看不到模块的内部结构,如果用 `require` 引入则没有问题。 |
| 21 | + |
| 22 | +经过更进一步的测试,甚至于这种写法也是不行的: |
| 23 | + |
| 24 | +```js |
| 25 | +let r = require |
| 26 | +let a = r('../test/a') |
| 27 | +``` |
| 28 | + |
| 29 | +在 VSCode 中会发现 `a` 的类型为 `any` 并且丢失了很多信息。 |
| 30 | + |
| 31 | +# 解决方案 |
| 32 | + |
| 33 | +换句话说,封装 `require` 的路是行不通了,只能用原生的 `require` 函数,那么只能看看有什么办法可以影响到模块查找的流程了。 |
| 34 | + |
| 35 | +内置的那套流程和顺序肯定是改不了,看起来只能从 `NODE_PATH` 这个全局变量下手了。我们知道 `require` 函数会去 `NODE_PATH` 的目录里查找模块,所以只要把它设置为工程的根路径,就可以实现绝对路径加载了。试验一下,项目目录如下所示: |
| 36 | + |
| 37 | +``` |
| 38 | +project |
| 39 | + |-----main |
| 40 | + |------index.js |
| 41 | + |-----util |
| 42 | + |------utils.js |
| 43 | +``` |
| 44 | + |
| 45 | +很简单的定义一下 `utils.js`,就导出一个对象: |
| 46 | + |
| 47 | +```js |
| 48 | +// utils.js |
| 49 | +module.exports = { |
| 50 | + key:' value' |
| 51 | +} |
| 52 | +``` |
| 53 | + |
| 54 | +在 `index.js` 中这么写: |
| 55 | + |
| 56 | +```js |
| 57 | +let utils = require('utils/utils') |
| 58 | +console.log(utils.key) |
| 59 | +``` |
| 60 | + |
| 61 | +然后执行 `node main/index.js`,肯定会编译失败。 |
| 62 | + |
| 63 | +但如果指定了 `NODE_PATH` 就不一样了,此时可以正常运行: |
| 64 | + |
| 65 | +```shell |
| 66 | +export NODE_PATH=$PWD && node main/index.js |
| 67 | +``` |
| 68 | + |
| 69 | +# 优化 |
| 70 | + |
| 71 | +直接在命令行中指定 `NODE_PATH` 有两个问题: |
| 72 | + |
| 73 | +1. 改变了项目的启动方式,别的开发者也会受到影响,不过这一点问题不大,因为一般都是通过命令来启动的。 |
| 74 | +2. 如果在不同的路径下启动 node,那么 `$PWD` 是会变的,这种方式不够安全。 |
| 75 | + |
| 76 | +所以比较好的做法是,在入口文件中指定 `NODE_PATH`,因为这个文件的路径一般不会改变。所以 `index.js` 可以改造成这样: |
| 77 | + |
| 78 | +```js |
| 79 | +let path = require('path') |
| 80 | +process.env.NODE_PATH = path.resolve(__dirname, '../') ; |
| 81 | +require('module').Module._initPaths(); |
| 82 | + |
| 83 | +let utils = require('utils/utils') |
| 84 | + |
| 85 | +console.log(utils.key) |
| 86 | +``` |
| 87 | + |
| 88 | +这种写法的好处在于,无论我们在哪里执行 `node path/to/index.js` 都会得到正确的结果。 |
| 89 | + |
| 90 | +最后还需要修正一下写 `require` 函数时,路径补全的问题,只要在根目录里面加上一个 `jsconfig.json` 文件并添加如下内容即可: |
| 91 | + |
| 92 | +```json |
| 93 | +{ |
| 94 | + "compilerOptions": { |
| 95 | + "target": "es6", |
| 96 | + "module": "commonjs", |
| 97 | + "baseUrl": "./", |
| 98 | + }, |
| 99 | + "exclude": [ |
| 100 | + "node_modules" |
| 101 | + ] |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +核心在于 `"baseUrl": "./"` 这一行。这样当我们写 `utils` 这个单词的时候,就可以享受到自动补全了。 |
| 106 | + |
| 107 | +至此,无论是 Node 的执行,还是路径补全,抑或是定义跳转功能,都正常工作了。 |
0 commit comments