这个周末,我开始看 Express.js
- 这个(应该是)Node.js
最老且最流行的网络框架了。Node.js
我已经用了几年了,但我从未真正构建过一个完整的网络应用。当我阅读的时候,遇到了从未见过的 – 一篇 StrongLoop
的文章 使用 require.resolve
来计算相对于模块的文件路径。以前我只用过模块的本地变量 __dirname
来计算相对于模块的文件路径,所以现在我想花点实践看看 require.resolve()
是如何工作的。
在 Node.js
里,模块加载器 - require()
- 可以使用相对于模块的路径。例如,在文件系统的任意目录,有一个模块需要加载自己的库,可以安全地使用相对于模块的文件路径来引用这个库:
|
|
不使用模块加载器的时候,文件路径一般要求是绝对或者相对于应用执行环境的根目录的。例如,读取文件内容时,需要给 fs.readFileSync
方法提供一个非本地的路径。我常使用 __dirname
变量作为计算非本地路径的方法:
|
|
这会产生一个由模块目录和提供的文件名拼接而成的路径。这种方法没毛病,但是,如果能更自然地计算相对于模块的文件路径就更好了。这也是 require.ensure()
吸引我眼球的原因。require.ensure()
使用和 require
相同的机制来加载模块 – 也就是说它可以使用相对于模块的路径;但是,它并不加载模块,而只是返回表明模块的文件路径。这个相对于模块的文件绝对路径就可以用来,比如,读取所代表模块的内容。
实践一下,创建一个简单的模块,来定位和读取同目录下的一个文件的内容。但是,为了确保不会创建相对于根目录的文件路径,本模块从子目录加载 demo。
|
|
然后,从子目录的 demo 模块,我使用 __dirname
和 require.resolve()
来生成相对于模块的文件路径:
|
|
可以看到,我使用这两种不同的方式只是定位并读入文件。然后,当我们运行根目录的 node
应用时,得到下图的输出:
可以看到,两种方法使用相对于模块的路径计算出了相同的绝对路径。而且两种方法都可以读取文件的内容。
起初,看上去此两种都可以生成相对于模块的文件路径。但 require.resolve()
有一些很重要的注意点。一开始,它就会和文件系统交互。因为 require.resolve()
使用和 require()
一样的模块解析算法,它实际上会定位模块。也就是说,它会查找文件系统来确认文件是否存在。这会增加处理负担(大概会比 __dirname
方法要多)。还有随之而来的第二个注意点:它只能用于存在的文件。这意味着,不能用 require.resolve()
来产生相对于模块的文件路径以保存新文件。
为了讲清我的意思,我将更新上例,加入一个多余的调用来解析一个并不存在的相对于模块的文件路径。
|
|
如你所见,我试图解析一个并不存在相对于模块的文件路径, 即 my-new-file.txt
。运行结果如下:
如你所见,整个 demo 崩溃就是因为我们试图解析一个不存在的文件的路径。模块解析算法冒泡似地展示出来了。
很明显,require.resolve()
所做的远不止生成文件路径那么简单–它实际上在和文件系统交互。正因如此,它会带来更大的处理负担,在使用方式上也有一些限制。但是,它能提供一种更自然的计算相对于模块的文件路径的方式。所以,可能在大部分情况下我想我不会用这种方法,但用来读取像设置文件那样的,只会读一次的文件,还是不错的。