NODE_PATH 对 node module 加载环境的重新规划
一个自认为的不爽点
自开始接触 node ,就被 node 的那个模块加载(引用)的方式一真膈应到现在: 对于非 node_modules 为什么不能像 PHP 甚至 JAVA 那样,默认指定一个模块加载的 basepath,而是只能用 ‘./’ 、’../’ 的相对路径来引用。 这样的话,引用路径有一定的耦合性,在代码结构重构的时候会带来很大不便。 (好,高大尚的理由说完了。。。 再说这样引用也真不好看啊,太 LOW 了点吧~ 在 require 的时候还好,现在用 import 真是不太协调啊~)
之前自创的非主流方法
在用 commonjs-module 的情况下,因为 require 支持动态引用,所以可以在程序入口处设置一个 global
的 BASE_PATH
, 这样,在以后每个模块的引用时,都用基于这个 base_path 的路径:
global.BASE_PATH = __dirname;
---
const module1 = require(global.BASE_PATH + '/dir/module1');
当然,这个不是这篇文章的重点,而且在 ES6 下,import
不支持动态引用,这种方法也就自然无效了。
完美的解决文案
过了好长时间我也再没有去关注这个问题,直到半年前写一个项目,要写一个调用 webpack 的 node 脚本,在看官网api时,顺便又看了一下 module 篇章。 多看了几眼就发现新大陆了:
这里面有提到,如果有设置 NODE_PATH
,node 加载非相对/绝对路径时,就会以所设定的 NODE_PATH
做为根路径,进行模块加载。
感觉就是我之前想要的那种情况啊~ 有希望了!
先搞一个简单的试验环境如下:
main.js
console.log(process.env);
const module2 = require('dir2/module2');
module2('main message');
module2.js
const module1_1 = require('dir1/dir1_1/module1_1');
module.exports = function (msg) {
module1_1(msg);
console.log(`This is ${msg} from module2 ~`);
}
module1-1.js
module.exports = function (msg) {
console.log(`This is ${msg} from module1-1 ~`);
}
接下来就是改 NODE_PATH
来试试行不行了,先打印 process.env
来看看默认的值是什么。
也可以在 shell 里直接输出
$NODE_PATH
,结果也是空。
里面就没有 NODE_PATH
这一项,难怪之前的 node 压根就不能以非相对路径的形式来调用模块了。那我们就给它加一个~~~。
粗暴直接的全局环境变量修改
NODE_PATH 肯定是一个环境变量了,那么最简单直接的方法就是在系统的环境变量中添加上这么一项就OK了。
找到自己用户的 .bashrc
文件,添加以下内容:
export NODE_PATH=/home/username/www/
再来试一下,还真行,不再显示 Error: Cannot find module 'dir2/module2'
这个讨厌的错误了。
说明 node 本身就提供了这么一种相对于某个路径的加载方式,只是大多数人都忽略了。
当然,这么粗暴的修改全局的环境变量,不是一个好办法,不可能你所有部署的 node 代码都适合于这个基本路径。下面就再介绍两种“动态”设置 NODE_PATH
的方法。
声明临时的环境变量
将不稳定的环境变量写死是挺不靠谱的,但是我们又需要它,那就只能是搞一下临时的了。
linux 下有两个命令 set
、declare
都可以在执行环境中写入环境变量,再通过连续命令符 &&
将命令连接起来就行。很简单,如下:
declare -x NODE_PATH=/Users/babytree/Documents/temp&& node main.js
或是
set NODE_PATH=/Users/babytree/Documents/temp&& node main.js
这样的话,每次启动时只需要在启动命令前加上 set
或 declare
命令就行,或是将这个写进 package.json
的 script
里,直接 run
就好。
设置子进程的环境变量
还有一种方法也是我现在实际用的方法,通过 child process
启动系统时动态写入一个 NODE_PATH
。
node 中 child_process.exec
、child_process.spawn
、child_process.fork
三个启动子进程的方法都可以带如下参数:
- cwd:
Current working directory of the child process - env:
其中的 env
就是给子进程设置环境信息的参数,我们只需在这里加上想要的 NODE_PATH
属性,就可以了。
const env = process.env;
env.NODE_PATH = path.resolve(__dirname, '../xxx');
child_process.spawn('node', ['--harmony', '--debug'], {env: env});
总结
通过如上的方法,你就可以在 node 中上用“绝对”路径的方法来引用模块了,在代码重构、项目代码共享方面都会有较大的进步空间。 而且,可以提供版本切换等需要,个人感觉还是很有实用价值的。
以后还是得仔细点看文档,在搞定这个问题的同时,我也解决了一个之前个人感觉挺恶心的问题: nodejs V6 不能用 import/export 的优雅解决方法