我想要求我的文件总是通过我的项目的根,而不是相对于当前模块。
例如,如果查看https://github.com/visionmedia/express/blob/2820f2227de0229c5d7f28009aa432f9f3a7b5f9/examples/downloads/app.js第6行,您将看到
express = require('../../')
在我看来,这真的很糟糕。假设我想让我所有的例子都只靠近根结点一层。这是不可能的,因为我必须更新超过30个例子,并且在每个例子中更新很多次。:
express = require('../')
我的解决方案是有一个基于根的特殊情况:如果字符串以$开头,那么它相对于项目的根文件夹。
任何帮助都是感激的,谢谢
更新2
现在我使用require.js,它允许你以一种方式编写,在客户端和服务器上都可以工作。Require.js还允许你创建自定义路径。
更新3
现在我转移到webpack + gulp,我使用enhanced-require来处理服务器端模块。看这里的基本原理:http://hackhat.com/p/110/module-loader-webpack-vs-requirejs-vs-browserify/
我认为你不需要用你描述的方式来解决这个问题。如果您想在大量文件中更改相同的字符串,请使用sed。在你的例子中,
find . -name "*.js" -exec sed -i 's/\.\.\/\.\.\//\.\.\//g' {} +
/../变成了../
或者,您可以要求配置文件存储包含库路径的变量。如果您将以下文件存储为config.js在示例目录中
var config = {};
config.path = '../../';
在你的例子文件中
myConfiguration = require('./config');
express = require(config.path);
您将能够从一个文件控制每个示例的配置。
这只是个人喜好。
手动符号链接(和Windows连接)
examples目录中不能包含一个node_modules,该node_modules带有指向项目根目录的符号链接吗?/因此允许示例使用require('project'),尽管这并没有删除映射,但它允许源代码使用require('project')而不是require('../../')。
我已经对此进行了测试,它确实适用于v0.6.18。
项目目录清单:
$ 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')。
自动化的符号链接
手动创建符号链接的问题是,每次你npm ci时,你都会丢失符号链接。如果您使符号链接进程成为依赖项,那么就没有问题。
模块basetag是一个postinstall脚本,它在每次运行npm install或npm ci时创建一个名为$的符号链接(或Windows连接):
npm install --save basetag
node_modules/$ -> ..
这样,您就不需要对代码或require系统进行任何特殊修改。$成为您可以从中进行请求的根。
var foo = require('$/lib/foo.js');
如果你不喜欢使用$,而更喜欢#或其他(除了@,这是npm的一个特殊字符),你可以fork它并进行更改。
注意:虽然Windows符号链接(到文件)需要管理员权限,但Windows连接(到目录)不需要管理员权限。这是一种安全、可靠、跨平台的解决方案。
大局
这看起来“真的很糟糕”,但要给它时间。事实上,它真的很好。显式的require()提供了完全的透明性和易于理解,就像项目生命周期中的一股新鲜空气。
可以这样想:你正在阅读一个例子,尝试Node.js,你认为它“在我看来真的很糟糕”。你在质疑Node.js社区的领导者,他们比任何人都花了更多的时间来编写和维护Node.js应用程序。作者犯这样一个新手错误的可能性有多大?(我同意,从我的Ruby和Python背景来看,乍一看这像是一场灾难。)
围绕Node.js有很多炒作和反炒作。但是当尘埃落定后,我们将承认显式模块和“本地优先”包是采用的主要驱动力。
一般情况
当然,首先搜索当前目录中的node_modules,然后是父目录、祖父目录、曾祖父目录等等。所以您已经安装的包已经以这种方式工作了。通常你可以从项目中的任何地方要求(“express”),它工作得很好。
如果您发现自己正在从项目的根目录加载公共文件(可能是因为它们是公共实用程序函数),那么这就是一个很大的线索,说明是时候创建包了。包非常简单:将文件移动到node_modules/,并放入package.json
在那里。瞧!整个项目都可以访问该名称空间中的所有内容。包是将代码放入全局名称空间的正确方法。
其他解决方法
我个人不使用这些技巧,但它们确实回答了你的问题,当然你比我更了解自己的情况。
您可以将$NODE_PATH设置为项目根目录。当您需要()时,将搜索该目录。
接下来,您可以折衷一下,从所有示例中要求一个通用的本地文件。该通用文件只是重新导出祖父目录中的真实文件。
Examples /downloads/app.js(以及其他类似的文件)
var express = require('./express')
/下载/ express.js例子
module.exports = require('../../')
现在当你重新定位这些文件时,最坏的情况是修复一个垫片模块。
恕我直言,最简单的方法是将自己的函数定义为GLOBAL对象的一部分。
在项目的根目录下创建projRequire.js,包含以下内容:
var projectDir = __dirname;
module.exports = GLOBAL.projRequire = function(module) {
return require(projectDir + module);
}
在你的主文件中,在需要任何特定于项目的模块之前:
// init projRequire
require('./projRequire');
之后,以下工作对我来说:
// main file
projRequire('/lib/lol');
// index.js at projectDir/lib/lol/index.js
console.log('Ok');
@Totty,我想出了另一个解决方案,可以解决你在评论中描述的情况。描述将是tl;dr,所以我最好展示我的测试项目的结构的图片。
我们准备尝试一种新的方法来解决这个问题。
以其他已知项目(如spring和guice)为例,我们将定义一个“context”对象,它将包含所有的“require”语句。
该对象将被传递给所有其他模块使用。
例如
var context = {}
context.module1 = require("./module1")( { "context" : context } )
context.module2 = require("./module2")( { "context" : context } )
这要求我们将每个模块编写为一个接收选项的函数,这对我们来说是一个最佳实践。
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定义为函数——这将允许我们根据环境需要不同的模块——但这超出了这个线程的范围。
这里有关于这个问题的很好的讨论。
我遇到了同样的架构问题:想要给我的应用程序更多的组织和内部名称空间,但没有:
将应用程序模块与外部依赖项混合,或者为特定于应用程序的代码使用私有NPM回购
使用相对要求,使得重构和理解更加困难
使用符号链接或更改节点路径,这可能会模糊源代码位置,并且不能很好地进行源代码控制
最后,我决定使用文件命名约定而不是目录来组织我的代码。结构应该是这样的:
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');
通过这种方式,所有应用程序代码都按层次结构组织成模块,相对于应用程序根,所有其他代码都可以使用。
当然,主要的缺点是在文件浏览器中,您不能展开/折叠树,就好像它实际上被组织成目录一样。但我喜欢它非常明确所有代码的来源,而且它没有使用任何“魔法”。
我喜欢为共享代码创建一个新的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'), node会找到它!
node_modules如何工作
Node有一个在竞争对手中独一无二的解决模块的聪明算法
平台。
如果你从/beep/boop/bar.js中require('./foo.js'), node会在/beep/boop/foo.js中寻找。/foo.js。以./或..开头的路径/对于调用require()的文件总是本地的。
然而,如果你需要一个非相对名称,例如require('xyz')来自/beep/boop/foo.js, node会按顺序搜索这些路径,在第一次匹配时停止,如果没有找到就会引发错误:
/beep/boop/node_modules/xyz
/beep/node_modules/xyz
/node_modules/xyz
对于每个存在的xyz目录,node将首先查找一个xyz/包。查看是否存在一个“main”字段。“main”字段定义了如果你需要()目录路径,哪个文件应该负责。
例如,如果/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中导出的文件将被返回
要求(“xyz”)。
如果没有包裹。Json或没有“main”字段,index.js是假设的:
/beep/node_modules/xyz/index.js
在Browserify手册中有一个非常有趣的章节:
avoiding ../../../../../../..
Not everything in an application properly belongs on the public npm
and the overhead of setting up a private npm or git repo is still
rather large in many cases. Here are some approaches for avoiding the
../../../../../../../ relative paths problem.
node_modules
People sometimes object to putting application-specific modules into
node_modules because it is not obvious how to check in your internal
modules without also checking in third-party modules from npm.
The answer is quite simple! If you have a .gitignore file that
ignores node_modules:
node_modules
You can just add an exception with ! for each of your internal
application modules:
node_modules/*
!node_modules/foo
!node_modules/bar
Please note that you can't unignore a subdirectory, if the parent is
already ignored. So instead of ignoring node_modules, you have to
ignore every directory inside node_modules with the
node_modules/* trick, and then you can add your exceptions.
Now anywhere in your application you will be able to require('foo')
or require('bar') without having a very large and fragile relative
path.
If you have a lot of modules and want to keep them more separate from
the third-party modules installed by npm, you can just put them all
under a directory in node_modules such as node_modules/app:
node_modules/app/foo
node_modules/app/bar
Now you will be able to require('app/foo') or require('app/bar')
from anywhere in your application.
In your .gitignore, just add an exception for node_modules/app:
node_modules/*
!node_modules/app
If your application had transforms configured in package.json, you'll
need to create a separate package.json with its own transform field in
your node_modules/foo or node_modules/app/foo component directory
because transforms don't apply across module boundaries. This will
make your modules more robust against configuration changes in your
application and it will be easier to independently reuse the packages
outside of your application.
symlink
Another handy trick if you are working on an application where you can
make symlinks and don't need to support windows is to symlink a lib/
or app/ folder into node_modules. From the project root, do:
ln -s ../lib node_modules/app
and now from anywhere in your project you'll be able to require files
in lib/ by doing require('app/foo.js') to get lib/foo.js.
custom paths
You might see some places talk about using the $NODE_PATH
environment variable or opts.paths to add directories for node and
browserify to look in to find modules.
Unlike most other platforms, using a shell-style array of path
directories with $NODE_PATH is not as favorable in node compared to
making effective use of the node_modules directory.
This is because your application is more tightly coupled to a runtime
environment configuration so there are more moving parts and your
application will only work when your environment is setup correctly.
node and browserify both support but discourage the use of
$NODE_PATH.
我喜欢做的是利用node从node_module目录加载的方式。
如果有人试图加载“thing”模块,他会做如下的事情
require('thing');
Node将在'node_module'目录中查找'thing'目录。
由于node_module通常位于项目的根,所以我们可以利用这种一致性。(如果node_module不在根节点上,那么您就会遇到其他令人头痛的问题。)
如果我们进入目录,然后从目录中返回,我们可以获得到节点项目根的一致路径。
require('thing/../../');
如果我们想访问/happy目录,我们会这样做。
require('thing/../../happy');
虽然这有点粗糙,但是我觉得如果node_modules加载的功能发生了变化,将会有更大的问题需要处理。这种行为应该保持一致。
为了使事情更清楚,我这样做,因为模块的名称并不重要。
require('root/../../happy');
我最近在angular2中使用了它。我想从根目录加载服务。
import {MyService} from 'root/../../app/services/http/my.service';
在简单的行中,你可以调用自己的文件夹为module:
为此,我们需要:global和app-module-path module
这里“App-module-path”是模块,它允许你添加额外的目录到Node.js模块搜索路径
global的意思是,你附加到这个对象的任何东西b在你的应用中都是可用的。
现在看一下这个片段:
global.appBasePath = __dirname;
require('app-module-path').addPath(appBasePath);
__dirname为节点当前运行目录。您可以在这里给出自己的路径来搜索模块的路径。
一些答案说,最好的方法是将代码作为包添加到node_module,我同意,这可能是最好的方法,失去../../../ in require,但实际上没有一个给出这样做的方法。
从2.0.0版本开始,你可以从本地文件安装一个包,这意味着你可以在根目录下创建一个文件夹,里面有你想要的所有包,
-modules
--foo
--bar
-app.js
-package.json
所以在包装上。Json,你可以添加模块(或foo和bar)作为一个包,而不需要发布或使用外部服务器,像这样:
{
"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
只是想继续Paolo Moretti和Browserify的精彩回答。如果你正在使用一个编译器(例如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目录。
符号链接
使用符号链接,我们可以摆脱一些嵌套级别:
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的警告:babel的——copy-files标志不能很好地处理符号链接。它可能会一直导航到…符号链接和隐性看到无尽的文件。一种变通方法是使用以下目录结构:
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将不再看到符号链接。
尝试使用asapp:
NPM安装——保存为app
https://www.npmjs.com/package/asapp
var { controller, helper, middleware, route, schema, model, APP, ROOT } = require('asapp')
Controller ('home')改为require('../../controllers/home)
另一个答案是:
想象一下这个文件夹结构:
node_modules
lodash
src
子目录
foo.js
bar.js
main.js
测试
. 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");
现在你可以使用。babelrc文件中的babel和babel插件模块解析器来配置两个根文件夹:
{
"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"
]
}
然后像这样在测试和SRC中导入文件:
import foo from "foo"
import bar from "bar"
import _ from "lodash"
如果你使用ES5语法,你可以使用asapp。对于ES6,你可以使用babel-plugin-module-resolver,使用如下配置文件:
.babelrc
{
"plugins": [
["module-resolver", {
"root": ["./"],
"alias": {
"app": "./app",
"config": "./app/config",
"schema": "./app/db/schemas",
"model": "./app/db/models",
"controller": "./app/http/controllers",
"middleware": "./app/http/middleware",
"route": "./app/http/routes",
"locale": "./app/locales",
"log": "./app/logs",
"library": "./app/utilities/libraries",
"helper": "./app/utilities/helpers",
"view": "./app/views"
}
}]
]
}
如果你使用yarn而不是npm,你可以使用工作区。
假设我有一个文件夹服务,我希望更容易地需要:
.
├── app.js
├── node_modules
├── test
├── services
│ ├── foo
│ └── bar
└── package.json
要创建Yarn工作空间,需要创建一个包。services文件夹中的Json文件:
{
"name": "myservices",
"version": "1.0.0"
}
在你的主包里。json添加:
"private": true,
"workspaces": ["myservices"]
从项目的根目录运行yarn install。
然后,在代码的任何地方,你可以这样做:
const { myFunc } = require('myservices/foo')
而不是像这样:
const { myFunc } = require('../../../../../../services/foo')
我正在寻找完全相同的简单性,要求文件从任何级别,我发现模块-别名。
安装:
npm i --save module-alias
打开你的包裹。Json文件,在这里你可以为你的路径添加别名,例如。
"_moduleAliases": {
"@root" : ".", // Application's root
"@deep" : "src/some/very/deep/directory/or/file",
"@my_module" : "lib/some-file.js",
"something" : "src/foo", // Or without @. Actually, it could be any string
}
使用你的别名只需:
require('module-alias/register')
const deep = require('@deep')
const module = require('something')
我实现这一点的方法是创建“本地链接模块”。
的文件夹结构为例
db ¬
models ¬
index.js
migrations
seed
config.json
routes ¬
index.js
user ¬
index.js
如果从。/routes/user/index.js我想访问/db/models/index.js我会写
require('../../db/models/index.js')
为了使/db/models/index.js可以从任何地方访问,我在db文件夹中创建了一个名为_module_的文件夹,其中包含一个包。Json和一个main.js文件。
# package.json
{
"name": "db", <-- change this to what you want your require name to be
"version": "1.0.0",
"description": "",
"author": "",
"repository": {},
"main": "main.js"
}
// main.js
module.exports = require('../../db/models/index');
main.js中的路径必须是相对的,就像文件在node_modules中一样
node_modules ¬
db ¬
main.js
然后你可以运行npm install ./db/_module_,这将把。/db/_module_中的文件复制到。/node_modules/db中,在应用程序包的依赖项下创建一个条目。json之类的
"db": "file:db/_module_"
您现在可以在任何地方使用此包
const db = require('db');
当你运行npm install时,它会自动安装你的其他模块,工作跨平台(没有符号链接),并且不需要第三方包。