如何使node.js需要绝对? (而不是亲戚)

问题描述 投票:203回答:34

我想要我的文件总是由我的项目的根目录而不是相对于当前模块。

例如,如果你看看https://github.com/visionmedia/express/blob/2820f2227de0229c5d7f28009aa432f9f3a7b5f9/examples/downloads/app.js第6行,你会看到

express = require('../../')

这真是糟糕的IMO。想象一下,我想把我的所有例子都放在根目录上,只有一个级别。这是不可能的,因为我必须在每个示例中更新超过30个示例和多次。对此:

express = require('../')

我的解决方案是基于root的特殊情况:如果一个字符串以$开头,那么它相对于项目的根文件夹。

任何帮助表示赞赏,谢谢

Update 2

现在我正在使用require.js,它允许您以一种方式编写,并在客户端和服务器上运行。 Require.js还允许您创建自定义路径.-«

Update 3

现在我转到webpack + gulp,我使用enhanced-require来处理服务器端的模块。在这里看到理由:http://hackhat.com/p/110/module-loader-webpack-vs-requirejs-vs-browserify/

node.js require
34个回答
153
投票

那怎么样:

var myModule = require.main.require('./path/to/module');

它需要文件,好像它是从主js文件中需要的,所以只要您的主js文件位于项目的根目录,它就能很好地工作......这是我所欣赏的。


8
投票

如果您使用纱线代替npm,您可以使用workspaces

假设我有一个文件夹services我希望更容易:

.
├── app.js
├── node_modules
├── test
├── services
│   ├── foo
│   └── bar
└── package.json

要创建Yarn工作区,请在package.json中创建services folder文件:

{
  "name": "myservices",
  "version": "1.0.0"
}

在你的主package.json中添加:

"private": true,
"workspaces": ["myservices"]

从项目的根目录运行yarn install

然后,在代码中的任何位置,您都可以:

const { myFunc } = require('myservices/foo')

而不是像:

const { myFunc } = require('../../../../../../services/foo')

7
投票

你可以使用我制作的模块,Undot。这不是什么先进的,只是一个帮手,所以你可以简单地避免那些点地狱。

例:

var undot = require('undot');
var User = undot('models/user');
var config = undot('config');
var test = undot('test/api/user/auth');

7
投票

一些答案是说最好的方法是将代码作为一个包添加到node_module中,我同意并且它可能是在require中丢失../../../的最佳方法,但它们实际上都没有给出这样做的方法。

从版本2.0.0你可以从本地文件安装一个包,这意味着你可以在你的根目录中创建你想要的所有包,

-modules
 --foo
 --bar 
-app.js
-package.json

所以在package.json中你可以添加modules(或foobar)作为包而无需发布或使用这样的外部服务器:

{
  "name": "baz",
  "dependencies": {
    "bar": "file: ./modules/bar",
    "foo": "file: ./modules/foo"
  }
}

之后你做了npm install,你可以使用var foo = require("foo")访问代码,就像你使用所有其他软件包一样。

更多信息可以在这里找到:

https://docs.npmjs.com/files/package.json#local-paths

在这里如何创建一个包:

https://docs.npmjs.com/getting-started/creating-node-modules


6
投票

您可以在app.js中定义类似的内容:

requireFromRoot = (function(root) {
    return function(resource) {
        return require(root+"/"+resource);
    }
})(__dirname);

然后,无论何时你想要从root用户那里获得某些东西,你只需使用requireFromRoot而不是vanilla require。到目前为止对我来说效果很好。


6
投票

我尝试过很多这样的解决方案。我最后将它添加到我的主文件的顶部(例如index.js):

process.env.NODE_PATH = __dirname;
require('module').Module._initPaths();

这会在加载脚本时将项目根添加到NODE_PATH。允许我通过引用项目根目录中的相对路径(例如var User = require('models/user'))来要求项目中的任何文件。只要您在项目根目录中运行主脚本,然后在项目中运行其他任何内容,此解决方案就应该可以运行。


5
投票

这是我做了6个多月的实际方式。我在项目中使用名为node_modules的文件夹作为我的根文件夹,这样它总是会从我称之为绝对需求的任何地方查找该文件夹:

  • node_modules 我的项目 index.js我可以要求(“myProject / someFolder / hey.js”)而不是require(“./ someFolder / hey.js”) someFolder包含hey.js

当您嵌套到文件夹中时,这更有用,如果以绝对方式设置,更改文件位置的工作要少得多。我只在我的whole app中使用2相对要求。


5
投票

Imho实现这一目标的最简单方法是在node_modules/app(或任何你称之为)的应用启动时创建一个指向../app的符号链接。然后你可以打电话给require("app/my/module")。所有主要平台都提供符号链接。

但是,您仍然应该将您的东西拆分为通过npm安装的较小的可维护模块。您也可以通过git-url安装私有模块,因此没有理由拥有一个单一的app-directory。


4
投票

在您自己的项目中,您可以修改根目录中使用的任何.js文件,并将其路径添加到process.env变量的属性中。例如:

// in index.js
process.env.root = __dirname;

之后您可以到处访问酒店:

// in app.js
express = require(process.env.root);

3
投票

不能examples目录包含一个node_modules与项目project -> ../../的根的符号链接,从而允许示例使用require('project'),虽然这不会删除映射,它确实允许源使用require('project')而不是require('../../')

我测试了这个,它确实适用于v0.6.18。

project目录列表:

$ ls -lR project
project:
drwxr-xr-x 3 user user 4096 2012-06-02 03:51 examples
-rw-r--r-- 1 user user   49 2012-06-02 03:51 index.js

project/examples:
drwxr-xr-x 2 user user 4096 2012-06-02 03:50 node_modules
-rw-r--r-- 1 user user   20 2012-06-02 03:51 test.js

project/examples/node_modules:
lrwxrwxrwx 1 user user 6 2012-06-02 03:50 project -> ../../

index.js的内容为exports对象的属性赋值,并调用console.log并显示一条消息,指出它是必需的。 test.js的内容是require('project')


3
投票

我喜欢做的是利用节点从node_module目录加载的方式。

如果一个人试图加载模块“东西”,人们会做类似的事情

require('thing');

然后,Node将在'node_module'目录中查找'thing'目录。

由于node_module通常位于项目的根目录下,因此我们可以利用此一致性。 (如果node_module不在根目录,那么你还有其他自我引发的问题需要处理。)

如果我们进入目录然后退出它,我们可以获得到节点项目根目录的一致路径。

require('thing/../../');

然后,如果我们想访问/ happy目录,我们会这样做。

require('thing/../../happy');

虽然它有点hacky,但我觉得如果node_modules如何加载更改的功能,将会有更大的问题需要处理。此行为应保持一致。

为了清楚起见,我这样做,因为模块的名称并不重要。

require('root/../../happy');

我最近用它来做角度2。我想从根加载服务。

import {MyService} from 'root/../../app/services/http/my.service';

124
投票

Browserify Handbook有一个非常有趣的部分:

避免../../../../../../ ..

并非应用程序中的所有内容都属于公共npm,并且在许多情况下设置私有npm或git repo的开销仍然很大。以下是一些避免../../../../../../../相对路径问题的方法。

node_modules

人们有时会反对将特定于应用程序的模块放入node_modules中,因为如果不检查npm中的第三方模块,如何检查内部模块并不明显。

答案很简单!如果你有一个忽略.gitignorenode_modules文件:

node_modules

您可以为每个内部应用程序模块添加!的异常:

node_modules/*
!node_modules/foo
!node_modules/bar

请注意,如果父项已被忽略,则无法取消对子目录的取消标记。因此,不必忽略node_modules,你必须使用node_modules技巧忽略node_modules/*中的每个目录,然后你可以添加你的异常。

现在,在您的应用程序的任何地方,您都可以使用require('foo')require('bar')而不会有非常大且脆弱的相对路径。

如果你有很多模块并希望将它们与npm安装的第三方模块分开,你可以将它们全部放在node_modules的目录下,例如node_modules/app

node_modules/app/foo
node_modules/app/bar

现在,您可以在应用程序的任何位置使用require('app/foo')require('app/bar')

在你的.gitignore中,只需为node_modules/app添加一个例外:

node_modules/*
!node_modules/app

如果您的应用程序在package.json中配置了转换,则需要在node_modules/foonode_modules/app/foo组件目录中创建一个单独的package.json及其自己的转换字段,因为转换不适用于模块边界。这将使您的模块对应用程序中的配置更改更加健壮,并且可以更轻松地在应用程序之外独立重用软件包。

symlink

如果您正在开发一个可以创建符号链接但不需要支持窗口的应用程序的另一个方便的技巧是将lib/app/文件夹符号链接到node_modules。从项目根目录,执行:

ln -s ../lib node_modules/app

现在,从项目的任何地方,您都可以通过lib/获取require('app/foo.js')来获取lib/foo.js中的文件。

custom paths

您可能会看到一些地方谈论使用$NODE_PATH环境变量或opts.paths为节点和browserify添加目录以查找模块。

与大多数其他平台不同,与有效使用$NODE_PATH目录相比,在node_modules中使用shell样式的路径目录数组并不像节点那样有利。

这是因为您的应用程序与运行时环境配置紧密耦合,因此有更多移动部件,您的应用程序仅在您的环境设置正确时才能工作。

node和browserify都支持但不鼓励使用$NODE_PATH


3
投票

另一个答案:

想象一下这个文件夹结构:

  • Nod_modls 洛兹
  • SRC 子目录 foo.js bar.js main.js
  • 测试 test.js

然后在test.js中,您需要这样的文件:

const foo = require("../src/subdir/foo");
const bar = require("../src/subdir/bar");
const main = require("../src/main");
const _ = require("lodash");

并在main.js中:

const foo = require("./subdir/foo");
const bar = require("./subdir/bar");
const _ = require("lodash");

现在你可以使用babelbabel-plugin-module-resolver和这个.babelrc文件来配置2个根文件夹:

{
    "plugins": [
        ["module-resolver", {
            "root": ["./src", "./src/subdir"]
        }]
    ]
}

现在,您可以在测试和src中以相同的方式要求文件:

const foo = require("foo");
const bar = require("bar");
const main = require("main");
const _ = require("lodash");

如果你想使用es6模块语法:

{
    "plugins": [
        ["module-resolver", {
            "root": ["./src", "./src/subdir"]
        }],
        "transform-es2015-modules-commonjs"
    ]
}

然后你导入test和src中的文件,如下所示:

import foo from "foo"
import bar from "bar"
import _ from "lodash"

2
投票

如果有人正在寻找另一种解决这个问题的方法,那么这是我自己对这项工作的贡献:

https://www.npmjs.com/package/use-import

基本思路:在项目的根目录中创建一个JSON文件,将文件路径映射到速记名称(或者让use-automapper为您完成)。然后,您可以使用这些名称请求您的文件/模块。像这样:

var use = require('use-import');
var MyClass = use('MyClass');

就是这样。


2
投票

我写了这个小包,它允许你从项目根目录的相对路径需要包,而不引入任何全局变量或覆盖节点默认值

https://github.com/Gaafar/pkg-require

它的工作原理如下

// create an instance that will find the nearest parent dir containing package.json from your __dirname
const pkgRequire = require('pkg-require')(__dirname);

// require a file relative to the your package.json directory 
const foo = pkgRequire('foo/foo')

// get the absolute path for a file
const absolutePathToFoo = pkgRequire.resolve('foo/foo')

// get the absolute path to your root directory
const packageRootPath = pkgRequire.root()

1
投票

我创建了一个名为“rekiure”的节点模块

它允许您在不使用相对路径的情况下进行要求

https://npmjs.org/package/rekuire

它非常易于使用


1
投票

我们即将尝试一种新的方法来解决这个问题。

从其他已知项目(如spring和guice)中获取示例,我们将定义一个“context”对象,该对象将包含所有“require”语句。

然后,此对象将传递给所有其他模块以供使用。

例如

var context = {}

context.module1 = require("./module1")( { "context" : context } )
context.module2 = require("./module2")( { "context" : context } )

这要求我们将每个模块编写为接收opts的函数,无论如何我们认为这是最佳实践。

module.exports = function(context){ ... }

然后你将参考上下文而不是要求东西。

var module1Ref = context.moduel1;

如果您愿意,可以轻松编写循环来执行require语句

var context = {};
var beans = {"module1" : "./module1","module2" : "./module2" }; 
for ( var i in beans ){
    if ( beans.hasOwnProperty(i)){
         context[i] = require(beans[i])(context);
    }
};

当您想要模拟(测试)时,这应该会让生活更轻松,并且在将代码作为包重用时可以解决您的问题。

您还可以通过将bean声明与其分离来重用上下文初始化代码。例如,您的main.js文件可能如此

var beans = { ... }; // like before
var context = require("context")(beans); // this example assumes context is a node_module since it is reused.. 

这种方法也适用于外部库,不需要在每次需要时对其名称进行硬编码 - 但是它需要特殊处理,因为它们的导出不是期望上下文的函数。

稍后我们还可以将bean定义为函数 - 这将允许我们根据环境将require设置为不同的模块 - 但它可以超出此线程的范围。


1
投票

我在同样的问题上遇到了麻烦,所以我写了一个名为include的软件包。

Include通过查找package.json文件来处理项目的根文件夹,然后将您提供给它的路径参数传递给本机require(),而不会出现所有相对路径混乱。我想这不是require()的替代品,而是需要处理非打包/非第三方文件或库的工具。就像是

var async = require('async'),
    foo   = include('lib/path/to/foo')

我希望这可能有用。


1
投票

如果您的应用程序的入口点js文件(即实际运行“节点”的文件)位于项目根目录中,则可以使用rootpath npm module轻松完成此操作。只需通过安装即可

npm install --save rootpath

...然后在入口点js文件的最顶部,添加:

require('rootpath')();

从那时起,所有需要调用现在都相对于项目根目录 - 例如require('../../../config/debugging/log');成为require('config/debugging/log');(配置文件夹位于项目根目录中)。


1
投票

在简单的行中,您可以将自己的文件夹称为模块:

为此我们需要:全局和app-module-path模块

这里的“App-module-path”是模块,它允许您向Node.js模块搜索路径添加其他目录。“global”是,您附加到此对象的任何内容都将在您的应用程序中随处可用。

现在看一下这个片段:

global.appBasePath = __dirname;

require('app-module-path').addPath(appBasePath);

__dirname是节点的当前运行目录。您可以在此处提供自己的路径来搜索模块的路径。


1
投票

只想跟进Paolo Moretti和Browserify的great answer。如果您正在使用转换器(例如,babel,typescript)并且您有源和转换代码的单独文件夹,如src/dist/,您可以使用解决方案的变体作为

node_modules

使用以下目录结构:

app
  node_modules
    ... // normal npm dependencies for app
  src
    node_modules
      app
        ... // source code
  dist
    node_modules
      app
        ... // transpiled code

然后你可以让babel等将src目录转移到dist目录。

symlink

使用符号链接我们可以摆脱一些嵌套级别:

app
  node_modules
    ... // normal npm dependencies for app
  src
    node_modules
      app // symlinks to '..'
    ... // source code
  dist
    node_modules
      app // symlinks to '..'
    ... // transpiled code

关于babel的警告--copy-files --copy-filesbabel标志不能很好地处理符号链接。它可能会继续导航到..符号链接并反复看到无穷无尽的文件。解决方法是使用以下目录结构:

app
  node_modules
    app // symlink to '../src'
    ... // normal npm dependencies for app
  src
    ... // source code
  dist
    node_modules
      app // symlinks to '..'
    ... // transpiled code

通过这种方式,src下的代码仍然会将app解析为src,而babel则不会再看到符号链接。


1
投票

刚遇到this article提到app-module-path。它允许您配置这样的基础:

require('app-module-path').addPath(baseDir);

66
投票

我喜欢为共享代码创建一个新的node_modules文件夹,然后让node和require做它最擅长的事情。

例如:

- node_modules // => these are loaded from your package.json
- app
  - node_modules // => add node-style modules
    - helper.js
  - models
    - user
    - car
- package.json
- .gitignore

例如,如果你在car/index.js,你可以require('helper')和节点会找到它!

How node_modules Work

node有一个聪明的算法来解析竞争平台中独有的模块。

如果你从require('./foo.js') /beep/boop/bar.js,节点将在./foo.js寻找/beep/boop/foo.js。以./../开头的路径始终是调用require()的文件的本地路径。

但是,如果您需要一个非相对名称,例如来自require('xyz')/beep/boop/foo.js,则节点按顺序搜索这些路径,在第一个匹配时停止并在未找到任何内容时引发错误:

/beep/boop/node_modules/xyz
/beep/node_modules/xyz
/node_modules/xyz

对于存在的每个xyz目录,node将首先查找xyz/package.json以查看是否存在"main"字段。如果"main"目录路径,require()字段定义哪个文件应该负责。

例如,如果/beep/node_modules/xyz是第一场比赛,/beep/node_modules/xyz/package.json有:

{
  "name": "xyz",
  "version": "1.2.3",
  "main": "lib/abc.js"
}

那么/beep/node_modules/xyz/lib/abc.js的出口将由require('xyz')退回。

如果没有package.json或没有"main"字段,则假设index.js

/beep/node_modules/xyz/index.js

0
投票

前段时间我创建了相对于预定义路径加载模块的模块。

https://github.com/raaymax/irequire

您可以使用它而不是require。

irequire.prefix('controllers',join.path(__dirname,'app/master'));
var adminUsersCtrl = irequire("controllers:admin/users");
var net = irequire('net');

也许对某人有用..


37
投票

大局

看起来“非常糟糕”,但要给它时间。事实上,它确实很好。明确的require()s提供了完全透明和易于理解,就像在项目生命周期中呼吸新鲜空气。

可以这样想:你正在阅读一个例子,将你的脚趾浸入Node.js,你已经确定它是“非常糟糕的IMO”。你是Node.js社区的第二猜测领导者,他们编写和维护Node.js应用程序的时间比任何人都多。作者犯这样一个菜鸟错误的可能性有多大? (我同意,从我的Ruby和Python背景来看,它最初看起来像是一场灾难。)

围绕Node.js有很多炒作和反炒作。但是当尘埃落定时,我们将承认明确的模块和“本地第一”软件包是采用的主要动力。

常见的情况

当然,node_modules来自当前目录,然后是父母,然后是祖父母,曾祖父母等。所以你安装的软件包已经以这种方式工作了。通常你可以从项目的任何地方require("express"),它工作正常。

如果您发现自己从项目的根目录加载公共文件(可能是因为它们是常用的实用程序功能),那么这是一个很大的线索,是时候制作一个包。包很简单:将文件移动到node_modules/并在那里放一个package.json。瞧!可以从整个项目中访问该命名空间中的所有内容。包是将代码放入全局命名空间的正确方法。

其他解决方法

我个人不使用这些技巧,但他们确实回答了你的问题,当然你比我更了解你自己的情况。

您可以将$NODE_PATH设置为项目根目录。当你require()时,将搜索该目录。

接下来,您可以妥协并要求所有示例中的公共本地文件。该公共文件只是重新导出祖父目录中的真实文件。

examples / downloads / app.js(和许多其他人一样)

var express = require('./express')

实例/下载/ express.js

module.exports = require('../../')

现在,当您重新定位这些文件时,最糟糕的情况是修复一个填充模块。


19
投票

看看node-rfr

这很简单:

var rfr = require('rfr');
var myModule = rfr('projectSubDir/myModule');

12
投票

恕我直言,最简单的方法是将自己的函数定义为GLOBAL对象的一部分。使用以下内容在项目的根目录中创建projRequire.js

var projectDir = __dirname;

module.exports = GLOBAL.projRequire = function(module) {
  return require(projectDir + module);
}

requireing任何项目特定模块之前的主文件中:

// init projRequire
require('./projRequire');

以下之后对我有用:

// main file
projRequire('/lib/lol');

// index.js at projectDir/lib/lol/index.js
console.log('Ok');


@Totty, I've comed up with another solution, which could work for case you described in comments. Description gonna be tl;dr, so I better show a picture with structure of my test project.

12
投票

我在我的项目中使用process.cwd()。例如:

var Foo = require(process.cwd() + '/common/foo.js');

可能值得注意的是,这将导致requireing一个绝对的路径,虽然我还没有遇到这个问题。


9
投票

有一个很好的讨论这个问题here

我遇到了同样的架构问题:想要一种给我的应用程序提供更多组织和内部命名空间的方法,而不需要:

  • 将应用程序模块与外部依赖关系混合,或者使用私有npm repos来处理特定于应用程序的代码
  • 使用相对需求,这使得重构和理解更难
  • 使用符号链接或更改节点路径,这可能会掩盖源位置,并且不能很好地与源控件一起使用

最后,我决定使用文件命名约定而不是目录来组织我的代码。结构看起来像:

  • NPM-shrinkwrap.json
  • 的package.json
  • node_modules ...
  • SRC app.js app.config.js app.models.bar.js app.models.foo.js app.web.js app.web.routes.js ...

然后在代码中:

var app_config = require('./app.config');
var app_models_foo = require('./app.models.foo');

要不就

var config = require('./app.config');
var foo = require('./app.models.foo');

和往常一样,node_modules可以使用外部依赖项:

var express = require('express');

通过这种方式,所有应用程序代码都按层次结构组织成模块,并且可用于相对于应用程序根目录的所有其他代码。

当然,主要的缺点是在文件浏览器中,您无法展开/折叠树,就好像它实际上已组织到目录中一样。但我喜欢它非常清楚所有代码的来源,并且它不使用任何“魔法”。


9
投票

假设您的项目root是当前工作目录,这应该工作:

// require built-in path module
path = require('path');

// require file relative to current working directory
config = require( path.resolve('.','config.js') );
© www.soinside.com 2019 - 2024. All rights reserved.