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

Tot*_*.js 220 require node.js

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

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

express = require('../../')
Run Code Online (Sandbox Code Playgroud)

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

express = require('../')
Run Code Online (Sandbox Code Playgroud)

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

任何帮助表示赞赏,谢谢

更新2

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

更新3

现在我转到webpack + gulp,我使用enhanced-require来处理服务器端的模块.请参阅此处的基本原理:http://hackhat.com/p/110/module-loader-webpack-vs-requirejs-vs-browserify/

cro*_*vel 155

那怎么样:

var myModule = require.main.require('./path/to/module');
Run Code Online (Sandbox Code Playgroud)

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

  • 如果使用像Mocha测试这样的单元测试代码,此解决方案将无效 (12认同)
  • 如果主要是您的项目的根源:) (5认同)
  • 如果你发现它太冗长,只需使用.bind():var rootReq = require.bind(require.main); rootReq('./ path/to/module'); (4认同)

Pao*_*tti 125

Browserify手册中有一个非常有趣的部分:

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

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

node_modules

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

答案很简单!如果您有一个.gitignore忽略的文件node_modules:

node_modules
Run Code Online (Sandbox Code Playgroud)

您只需!为每个内部应用程序模块添加一个例外:

node_modules/*
!node_modules/foo
!node_modules/bar
Run Code Online (Sandbox Code Playgroud)

请注意,如果父项已被忽略,则无法取消对子目录的取消标记.所以,不是忽略的node_modules,你必须忽略每一个目录 node_modulesnode_modules/*技巧,然后您可以添加您的例外.

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

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

node_modules/app/foo
node_modules/app/bar
Run Code Online (Sandbox Code Playgroud)

现在,您将能够require('app/foo')require('app/bar') 在应用程序的任何地方.

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

node_modules/*
!node_modules/app
Run Code Online (Sandbox Code Playgroud)

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

符号链接

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

ln -s ../lib node_modules/app
Run Code Online (Sandbox Code Playgroud)

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

自定义路径

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

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

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

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

  • 把它放在`node_modules`文件夹中的唯一不利方面就是它让nuke(`rm -rf node_modules`)文件夹变得更难 (17认同)
  • @Michael没那么难:git clean -dx node_modules (13认同)
  • 或者,如果您忘记了`git clean`语法,可以始终使用`rm -rf node_modules && git checkout node_modules` - 如果`node_modules`子目录有任何更改,请务必使用`git stash`. (3认同)
  • 我喜欢使用 node_modules 的想法,但考虑到它的易变性,我不喜欢存储源代码。将分离出来的模块发布出来并作为依赖保存在原项目中不是更有意义吗?它为 node_modules 目录的易变性提供了明确的解决方案,并且仅依赖于 npm,而不是依赖于 git、符号链接或 $NODE_PATH 解决方案。 (3认同)
  • NODE_PATH 看起来是要走的路。“您的应用程序只有在您的环境设置正确时才能工作”这始终是正确的!获得环境设置(通常在一个文件中)是否比更改每个文件中的每个导入更容易? (2认同)

Bla*_*son 67

我喜欢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
Run Code Online (Sandbox Code Playgroud)

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

node_modules如何工作

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

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

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

/beep/boop/node_modules/xyz
/beep/node_modules/xyz
/node_modules/xyz
Run Code Online (Sandbox Code Playgroud)

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

例如,if /beep/node_modules/xyz是第一个匹配并且/beep/node_modules/xyz/package.json具有:

{
  "name": "xyz",
  "version": "1.2.3",
  "main": "lib/abc.js"
}
Run Code Online (Sandbox Code Playgroud)

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

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

/beep/node_modules/xyz/index.js
Run Code Online (Sandbox Code Playgroud)

  • 关于加载模块时它是如何工作的很好的解释 (3认同)
  • 这是一个非常优雅的解决方案,避免了上述答案中的所有问题。应该考虑答案,恕我直言。 (3认同)

Jas*_*ith 36

大局

看起来"非常糟糕",但要给它时间.事实上,它确实很好.明确的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')
Run Code Online (Sandbox Code Playgroud)

实例/下载/ express.js

module.exports = require('../../')
Run Code Online (Sandbox Code Playgroud)

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

  • "你是Node.js社区的第二猜测领导者"请避免这种令人沮丧的语气. (28认同)
  • "你是Node.js社区的第二猜测领导者" - 同样的领导者决定使用回调而不是期货/承诺.我的大多数nodejs咨询涉及诅咒所谓的"领导者",并说服人们转向JVM.使用nodejs几个月后哪个更容易:) (21认同)
  • 该死的他是第二个猜测节点领导者.这就是行业发展的方式.如果节点人员没有再次猜测支持基于线程的并发模型的领导者,我们就不会有节点. (14认同)
  • 我同意Node.js的人必须选择相对要求是有原因的.我无法从你的答案中看出它的优点.它对我来说仍然感觉"不好";) (13认同)
  • @nirth,转移到JVM?看在上帝的份上,为什么? (8认同)
  • 我已经"给它时间",差不多4年后(现在是2016年)我读过许多代码库,在很多地方都有这个杂乱的"../../../",但更特别是在测试文件中.他们都试图摆脱它......还有"你是第二个猜测Node.js社区的领导者,那些编写和维护Node.js应用程序的人数比任何人都要多的人"是一种常见的"权威诉求" "谬误("你说过,因为权威人士认为某些东西,因此必须是真的."),请查看https://yourlogicalfallacyis.com/appeal-to-authority了解更多信息. (6认同)
  • @Ivancho Scala和Clojure :)虽然对于当前项目我们决定使用Python. (3认同)

war*_*sea 20

看看node-rfr.

这很简单:

var rfr = require('rfr');
var myModule = rfr('projectSubDir/myModule');
Run Code Online (Sandbox Code Playgroud)

  • 我试过了,它 rfr 可以用 node 执行,但它破坏了 VS Code 的代码导航......我一直无法找到解决方法,无法在 VS 中使用自动完成...... (3认同)

Ale*_*kii 12

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

var projectDir = __dirname;

module.exports = GLOBAL.projRequire = function(module) {
  return require(projectDir + module);
}
Run Code Online (Sandbox Code Playgroud)

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

// init projRequire
require('./projRequire');
Run Code Online (Sandbox Code Playgroud)

以下之后对我有用:

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

// index.js at projectDir/lib/lol/index.js
console.log('Ok');
Run Code Online (Sandbox Code Playgroud)


@Totty,我已经解决了另一个解决方案,这可能适用于您在评论中描述的案例.描述将是tl;dr,所以我更好地展示了我的测试项目结构的图片.


Wal*_*man 12

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

var Foo = require(process.cwd() + '/common/foo.js');
Run Code Online (Sandbox Code Playgroud)

值得注意的是,这将导致require绝对路径,尽管我还没有遇到这个问题.

  • 这是个坏主意,因为 CWD 不必与保存应用程序的目录相同。 (2认同)

pro*_*eta 9

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

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

// require file relative to current working directory
config = require( path.resolve('.','config.js') );
Run Code Online (Sandbox Code Playgroud)

  • @cespon没有那只是相对于文件要求. (6认同)

ind*_*lit 9

有这个问题的一个很好的讨论在这里.

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

  • 将应用程序模块与外部依赖关系混合,或者使用私有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');
Run Code Online (Sandbox Code Playgroud)

要不就

var config = require('./app.config');
var foo = require('./app.models.foo');
Run Code Online (Sandbox Code Playgroud)

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

var express = require('express');
Run Code Online (Sandbox Code Playgroud)

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

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


cyb*_*bat 9

如果您使用yarn而不是npm,则可以使用工作区

假设我有一个services我希望更轻松地需要的文件夹:

.
??? app.js
??? node_modules
??? test
??? services
?   ??? foo
?   ??? bar
??? package.json
Run Code Online (Sandbox Code Playgroud)

要创建Yarn工作区,请package.json在内创建一个文件services folder

{
  "name": "myservices",
  "version": "1.0.0"
}
Run Code Online (Sandbox Code Playgroud)

在您的主要package.json中添加:

"private": true,
"workspaces": ["myservices"]
Run Code Online (Sandbox Code Playgroud)

yarn install从项目的根目录运行。

然后,您可以在代码中的任何地方执行以下操作:

const { myFunc } = require('myservices/foo')
Run Code Online (Sandbox Code Playgroud)

而不是这样的:

const { myFunc } = require('../../../../../../services/foo')
Run Code Online (Sandbox Code Playgroud)

  • 也许这是一个想法,以阐明这“仅适用于纱线”而不适用于npm?我认为它也可能适用于npm,因此花了一些时间想知道自己做错了什么,直到尝试使用yarn为止。可能是一个愚蠢的假设,但也许我并不是唯一的一个。 (3认同)
  • 我已经编辑了一些以澄清。对困惑感到抱歉。 (2认同)
  • [npm 7 也理解工作空间](https://docs.npmjs.com/cli/v7/using-npm/workspaces)。 (2认同)

sen*_*tor 7

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

process.env.NODE_PATH = __dirname;
require('module').Module._initPaths();
Run Code Online (Sandbox Code Playgroud)

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


小智 7

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

例:

var undot = require('undot');
var User = undot('models/user');
var config = undot('config');
var test = undot('test/api/user/auth');
Run Code Online (Sandbox Code Playgroud)


Yan*_*kiy 7

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

从版本开始,2.0.0您可以从本地文件安装包,这意味着您可以在根目录中创建包含所需包的所有包,

-modules
 --foo
 --bar 
-app.js
-package.json
Run Code Online (Sandbox Code Playgroud)

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

{
  "name": "baz",
  "dependencies": {
    "bar": "file: ./modules/bar",
    "foo": "file: ./modules/foo"
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

在这里如何创建一个包:

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


Tro*_*ers 7

另一个答案:

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

  • 节点模块
    • 洛达什
  • 源文件
    • 子目录
      • foo.js
      • bar.js
    • 主文件
  • 测试

    • 测试.js

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

const foo = require("../src/subdir/foo");
const bar = require("../src/subdir/bar");
const main = require("../src/main");
const _ = require("lodash");
Run Code Online (Sandbox Code Playgroud)

main.js 中

const foo = require("./subdir/foo");
const bar = require("./subdir/bar");
const _ = require("lodash");
Run Code Online (Sandbox Code Playgroud)

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

{
    "plugins": [
        ["module-resolver", {
            "root": ["./src", "./src/subdir"]
        }]
    ]
}
Run Code Online (Sandbox Code Playgroud)

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

const foo = require("foo");
const bar = require("bar");
const main = require("main");
const _ = require("lodash");
Run Code Online (Sandbox Code Playgroud)

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

{
    "plugins": [
        ["module-resolver", {
            "root": ["./src", "./src/subdir"]
        }],
        "transform-es2015-modules-commonjs"
    ]
}
Run Code Online (Sandbox Code Playgroud)

然后像这样在测试src 中导入文件:

import foo from "foo"
import bar from "bar"
import _ from "lodash"
Run Code Online (Sandbox Code Playgroud)


use*_*684 6

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

requireFromRoot = (function(root) {
    return function(resource) {
        return require(root+"/"+resource);
    }
})(__dirname);
Run Code Online (Sandbox Code Playgroud)

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


Dan*_* D. 5

手动符号链接(和 Windows 连接)

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 -> ../../
Run Code Online (Sandbox Code Playgroud)

的内容为对象index.js的属性分配一个值,exportsconsole.log使用说明它是必需的消息进行调用。的内容test.jsrequire('project')

自动符号链接

手动创建符号链接的问题是每次你npm ci都会丢失符号链接。如果您使符号链接过程成为依赖项,中提琴,没问题。

该模块basetag是一个安装后脚本,它在$每次运行npm installnpm ci运行时创建一个符号链接(或 Windows 连接):

$ 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 -> ../../
Run Code Online (Sandbox Code Playgroud)
npm install --save basetag
Run Code Online (Sandbox Code Playgroud)

有了这个,您不需要对代码或要求系统进行任何特殊修改。$成为您可以要求的根。

node_modules/$ -> ..
Run Code Online (Sandbox Code Playgroud)

如果您不喜欢使用$and 更喜欢#或其他东西(除了@,这是 npm 的一个特殊字符),您可以分叉它并进行更改。

注意:虽然 Windows 符号链接(到文件)需要管理员权限,但 Windows 连接(到目录)不需要 Windows 管理员权限。这是一个安全、可靠、跨平台的解决方案。


Tot*_*.js 5

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

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

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

  • 恕我直言,node_modules文件夹仅适用于node_modules.将整个项目放在该文件夹中并不是一个好习惯. (31认同)
  • 我使用类似的方法,除了我在`/ src`中添加local(project)的`node_modules`,并为供应商留下`/ node_modules`以保持独立.所以我有本地代码的`/ src/node_modules`和供应商的`/ node_modules`. (4认同)
  • @cspiegl您可以使用`NODE_PATH`环境变量 (3认同)
  • @McSas你会建议什么作为替代方案获得与上述相同的效果? (2认同)

Joh*_*ald 5

恕我直言,实现此目标的最简单方法是在应用启动时创建一个指向的符号链接node_modules/app(或您所说的任何名称),该链接指向../app。然后,您可以致电require("app/my/module")。符号链接在所有主要平台上均可用。

但是,您仍然应该将内容拆分成较小的,可维护的模块,这些模块通过npm安装。您也可以通过git-url安装私有模块,因此没有理由拥有一个单一的应用程序目录。


小智 5

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

// in index.js
process.env.root = __dirname;
Run Code Online (Sandbox Code Playgroud)

之后,您可以在任何地方访问该属性:

// in app.js
express = require(process.env.root);
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

157340 次

最近记录:

5 年,11 月 前