Làm thế nào để tạo node.js yêu cầu tuyệt đối? (thay vì tương đối)


234

Tôi muốn yêu cầu các tệp của tôi luôn luôn bằng gốc của dự án của tôi và không liên quan đến mô-đun hiện tại.

Ví dụ: nếu bạn xem https://github.com/visionmedia/express/blob/2820f2227de0229c5d7f28009aa432f9f3a7b5f9/examples/doads/app.js dòng 6 bạn sẽ thấy

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

Đó là IMO thực sự xấu. Hãy tưởng tượng tôi muốn đặt tất cả các ví dụ của tôi gần với gốc chỉ bằng một cấp độ. Điều đó là không thể, bởi vì tôi sẽ phải cập nhật hơn 30 ví dụ và nhiều lần trong mỗi ví dụ. Về điều này:

express = require('../')

Giải pháp của tôi sẽ là có một trường hợp đặc biệt cho root dựa trên: nếu một chuỗi bắt đầu bằng $ thì nó liên quan đến thư mục gốc của dự án.

Bất kỳ trợ giúp được đánh giá cao, cảm ơn

Cập nhật 2

Bây giờ tôi đang sử dụng allow.js cho phép bạn viết theo một cách và hoạt động cả trên máy khách và máy chủ. Require.js cũng cho phép bạn tạo các đường dẫn tùy chỉnh.

Cập nhật 3

Bây giờ tôi đã chuyển sang webpack + gulp và tôi sử dụng các yêu cầu nâng cao để xử lý các mô-đun ở phía máy chủ. Xem ở đây lý do: http://hackhat.com/p/110/module-loader-webpack-vs-requirejs-vs-browserify/


Nếu bạn từng quyết định sử dụng hằng số / biến gốc rõ ràng, câu trả lời này hoạt động cho điều đó . Giải pháp sử dụng một mô-đun github nhỏ để xác định đường dẫn gốc.
steampowered

Câu trả lời:


162

Và thế còn:

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

Nó yêu cầu tệp như thể nó được yêu cầu từ tệp js chính, vì vậy nó hoạt động khá tốt miễn là tệp js chính của bạn nằm ở gốc của dự án của bạn ... và đó là điều tôi đánh giá cao.


Một ý tưởng không tồi (: Sau đó, bạn có thể định nghĩa một số phương thức khác để ánh xạ lại ứng dụng trong mô-đun request.main của mình. Tôi nghĩ rằng sau đó bạn có thể yêu cầu.main.req ('client / someMod'). dài dòng hơn yêu cầu hiện tại của tôi. Ngoài ra tôi không nghĩ là có giá trị vì tôi cũng không thích trình duyệt vì các thay đổi không tức thời và bỏ lỡ các thay đổi (vì mã của tôi sẽ chạy cả trong trình duyệt và node.js).
Totty.js

4
Nếu bạn thấy nó quá dài dòng, chỉ cần sử dụng .bind (): var rootReq = allow.bind (allow.main); rootReq ('./path/to/module');
cronvel

vâng, điều này có thể hữu ích cho ai đó vẫn muốn sử dụng browserify cho phía máy khách. Đối với tôi không còn cần thiết nữa, nhưng dù sao cũng cảm ơn câu trả lời của bạn (:
Totty.js

6
NẾU CHÍNH LÀ TRONG ROOT CỦA DỰ ÁN CỦA BẠN :)
Alexander Mills

12
Giải pháp này sẽ không hoạt động nếu mã được bao phủ bởi các thử nghiệm đơn vị như thử nghiệm Mocha
alx lark

129

Có một phần thực sự thú vị trong Cẩm nang Browserify :

tránh ../../../../../../ ..

Không phải tất cả mọi thứ trong một ứng dụng đều thuộc về npm công cộng và chi phí thiết lập một npm hoặc git repo riêng vẫn còn khá lớn trong nhiều trường hợp. Dưới đây là một số cách tiếp cận để tránh ../../../../../../../vấn đề đường dẫn tương đối.

nút_modules

Mọi người đôi khi phản đối việc đặt các mô-đun dành riêng cho ứng dụng vào node_modules vì ​​không rõ ràng làm thế nào để kiểm tra các mô-đun bên trong của bạn mà không kiểm tra các mô-đun của bên thứ ba từ npm.

Câu trả lời khá đơn giản! Nếu bạn có một .gitignoretập tin bỏ qua node_modules:

node_modules

Bạn chỉ có thể thêm một ngoại lệ !cho mỗi mô-đun ứng dụng nội bộ của mình:

node_modules/*
!node_modules/foo
!node_modules/bar

Xin lưu ý rằng bạn không thể unignore một thư mục con, nếu phụ huynh đã được bỏ qua. Vì vậy, thay vì bỏ qua node_modules, bạn phải bỏ qua mọi thư mục bên trong node_modules với node_modules/*thủ thuật, và sau đó bạn có thể thêm ngoại lệ của mình.

Bây giờ bất cứ nơi nào trong ứng dụng của bạn, bạn sẽ có thể require('foo') hoặc require('bar')không có một đường dẫn tương đối rất lớn và dễ vỡ.

Nếu bạn có nhiều mô-đun và muốn tách chúng ra khỏi các mô-đun của bên thứ ba được cài đặt bởi npm, bạn chỉ có thể đặt tất cả chúng trong một thư mục node_modulesnhư node_modules/app:

node_modules/app/foo
node_modules/app/bar

Bây giờ bạn sẽ có thể require('app/foo')hoặc require('app/bar') từ bất cứ nơi nào trong ứng dụng của bạn.

Trong của bạn .gitignore, chỉ cần thêm một ngoại lệ cho node_modules/app:

node_modules/*
!node_modules/app

Nếu ứng dụng của bạn đã biến đổi cấu hình trong package.json, bạn sẽ cần phải tạo ra một package.json riêng biệt với riêng của mình chuyển trường trong của bạn node_modules/foohoặc node_modules/app/foothư mục phần vì biến đổi không áp dụng qua các biên giới mô-đun. Điều này sẽ làm cho các mô-đun của bạn mạnh mẽ hơn trước các thay đổi cấu hình trong ứng dụng của bạn và việc sử dụng lại các gói bên ngoài ứng dụng của bạn sẽ dễ dàng hơn.

liên kết tượng trưng

Một mẹo hữu ích khác nếu bạn đang làm việc trên một ứng dụng mà bạn có thể tạo liên kết tượng trưng và không cần hỗ trợ các cửa sổ là liên kết một lib/ hoặc app/thư mục vào node_modules. Từ gốc dự án, làm:

ln -s ../lib node_modules/app

và bây giờ từ bất cứ nơi nào trong dự án của bạn, bạn sẽ có thể yêu cầu các tệp lib/bằng cách thực hiện require('app/foo.js')để có được lib/foo.js.

đường dẫn tùy chỉnh

Bạn có thể thấy một số nơi nói về việc sử dụng $NODE_PATH biến môi trường hoặc opts.pathsđể thêm các thư mục cho nút và trình duyệt để tìm trong các mô-đun.

Không giống như hầu hết các nền tảng khác, sử dụng một mảng các thư mục đường dẫn kiểu shell với $NODE_PATHnút không thuận lợi so với việc sử dụng hiệu quả node_modulesthư mục.

Điều này là do ứng dụng của bạn được kết hợp chặt chẽ hơn với cấu hình môi trường thời gian chạy nên có nhiều phần chuyển động hơn và ứng dụng của bạn sẽ chỉ hoạt động khi môi trường của bạn được thiết lập chính xác.

nút và trình duyệt cả hai hỗ trợ nhưng không khuyến khích sử dụng $NODE_PATH.


17
Mặt trái duy nhất của việc đặt nó vào node_modulesthư mục là nó làm cho rm -rf node_modulesthư mục nuke ( ) khó hơn
Michael

13
@Michael Không khó hơn nhiều: git clean -dx node_modules
Peter Wilkinson

3
Hoặc trong trường hợp bạn quên git cleancú pháp, người ta luôn có thể rm -rf node_modules && git checkout node_modules- chắc chắn git stashtrong trường hợp có bất kỳ thay đổi nào đối với các node_modulesthư mục con.
derenio

1
Tôi thích ý tưởng sử dụng node_modules, nhưng không phải để lưu trữ mã nguồn xem xét mức độ biến động của nó. Sẽ không có ý nghĩa hơn khi xuất bản mô-đun tách biệt và lưu nó dưới dạng phụ thuộc trong dự án ban đầu? Nó cung cấp một giải pháp rõ ràng cho sự biến động của thư mục node_modules và chỉ dựa vào npm, thay vì dựa vào git, các liên kết tượng trưng hoặc giải pháp $ NODE_PATH.
Kevin Koshiol

1
NODE_PATH trông giống như con đường để đi. "Ứng dụng của bạn sẽ chỉ hoạt động khi môi trường của bạn được thiết lập chính xác" điều này luôn đúng! Không dễ dàng hơn để thiết lập môi trường (thường là trong một tệp) hơn là thay đổi mỗi lần nhập trong mỗi tệp?
CpILL

73

Tôi muốn tạo một node_modulesthư mục mới cho mã được chia sẻ, sau đó để nút và yêu cầu làm những gì nó làm tốt nhất.

ví dụ:

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

Ví dụ: nếu bạn ở trong car/index.jsbạn có thể require('helper')và nút sẽ tìm thấy nó!

Cách thức hoạt động của node_modules

nút có một thuật toán thông minh để giải quyết các mô-đun duy nhất trong số các nền tảng đối thủ.

Nếu bạn require('./foo.js')từ /beep/boop/bar.js, nút sẽ tìm kiếm ./foo.jstrong /beep/boop/foo.js. Các đường dẫn bắt đầu bằng một ./hoặc ../luôn luôn cục bộ với tệp gọi require().

Tuy nhiên, nếu bạn yêu cầu một tên không liên quan, chẳng hạn như require('xyz')từ /beep/boop/foo.js, nút sẽ tìm kiếm các đường dẫn này theo thứ tự, dừng lại ở trận đấu đầu tiên và đưa ra lỗi nếu không tìm thấy gì:

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

Đối với mỗi xyzthư mục tồn tại, trước tiên, nút sẽ tìm kiếm xyz/package.jsonđể xem nếu một "main"trường tồn tại. Các "main"định nghĩa lĩnh vực mà tập tin nên chịu trách nhiệm nếu bạn require()đường dẫn thư mục.

Ví dụ: nếu /beep/node_modules/xyzlà trận đấu đầu tiên và /beep/node_modules/xyz/package.jsoncó:

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

sau đó xuất khẩu từ /beep/node_modules/xyz/lib/abc.jssẽ được trả lại bởi require('xyz').

Nếu không có package.jsonhoặc không có "main"trường, index.jsđược giả sử:

/beep/node_modules/xyz/index.js

2
giải thích tuyệt vời về cách thức hoạt động của nó khi tải một mô-đun
bắt đầu từ

2
Đây là một giải pháp rất thanh lịch, tránh tất cả các vấn đề trong các câu trả lời ở trên. Nên xem xét câu trả lời, imho.
Rodurico

38

Bức tranh lớn

Có vẻ như "thực sự xấu" nhưng hãy cho nó thời gian. Trên thực tế, nó thực sự tốt. Các require()s rõ ràng cung cấp một sự minh bạch hoàn toàn và dễ hiểu giống như một luồng không khí trong lành trong vòng đời dự án.

Hãy nghĩ về nó theo cách này: Bạn đang đọc một ví dụ, nhúng ngón chân vào Node.js và bạn đã quyết định đó là "IMO thực sự tồi tệ". Bạn là nhà lãnh đạo đoán thứ hai của cộng đồng Node.js, những người đã đăng nhập nhiều giờ để viết và duy trì các ứng dụng Node.js hơn bất kỳ ai. Cơ hội nào tác giả đã làm một sai lầm tân binh như vậy? (Và tôi đồng ý, từ nền tảng Ruby và Python của tôi, thoạt nhìn có vẻ như một thảm họa.)

Có rất nhiều sự cường điệu và phản đối xung quanh Node.js. Nhưng khi bụi lắng xuống, chúng tôi sẽ thừa nhận rằng các mô-đun rõ ràng và các gói "đầu tiên cục bộ" là động lực chính của việc áp dụng.

Trường hợp phổ biến

Tất nhiên, node_modulestừ thư mục hiện tại, sau đó cha mẹ, sau đó ông bà, ông bà, vv được tìm kiếm. Vì vậy, các gói bạn đã cài đặt đã hoạt động theo cách này. Thông thường bạn có thể require("express")từ bất cứ nơi nào trong dự án của bạn và nó hoạt động tốt.

Nếu bạn thấy mình đang tải các tệp phổ biến từ thư mục gốc của dự án (có lẽ vì chúng là các hàm tiện ích phổ biến), thì đó là một đầu mối lớn mà đã đến lúc tạo ra một gói. Các gói rất đơn giản: di chuyển các tệp của bạn vào node_modules/và đặt package.json ở đó. Voila! Mọi thứ trong không gian tên đó đều có thể truy cập được từ toàn bộ dự án của bạn. Các gói là cách chính xác để đưa mã của bạn vào một không gian tên toàn cầu.

Cách giải quyết khác

Cá nhân tôi không sử dụng những kỹ thuật này, nhưng họ trả lời câu hỏi của bạn và tất nhiên bạn biết rõ tình huống của mình hơn tôi.

Bạn có thể thiết lập $NODE_PATHđể root dự án của bạn. Thư mục đó sẽ được tìm kiếm khi bạn require().

Tiếp theo, bạn có thể thỏa hiệp và yêu cầu một tệp chung, cục bộ từ tất cả các ví dụ của bạn. Tập tin phổ biến đó chỉ đơn giản là xuất lại tập tin thật trong thư mục ông bà.

ví dụ / lượt tải xuống / app.js (và nhiều người khác thích nó)

var express = require('./express')

ví dụ / lượt tải xuống / express.js

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

Bây giờ khi bạn di chuyển các tệp đó, trường hợp xấu nhất là sửa mô-đun shim .


14
Tôi đồng ý rằng các chàng trai Node.js phải chọn yêu cầu tương đối vì một lý do. Tôi chỉ không thể nhìn thấy lợi thế của nó, từ câu trả lời của bạn. Nó vẫn cảm thấy "tệ" với tôi;)
Adam Schmideg 26/07/13

21
Bạn là nhà lãnh đạo đoán thứ hai của cộng đồng Node.js - Các nhà lãnh đạo tương tự đã quyết định sử dụng các cuộc gọi lại thay vì tương lai / lời hứa. Phần lớn các tư vấn của nodejs của tôi liên quan đến việc chửi bới "các nhà lãnh đạo" và thuyết phục mọi người chuyển sang JVM. Điều này dễ dàng hơn nhiều sau vài tháng sử dụng nodejs :)
David Sergey

8
@nirth, chuyển sang JVM? Vì Chúa, tại sao?
Ivancho

31
"Bạn là nhà lãnh đạo đoán thứ hai của cộng đồng Node.js", vui lòng tránh giai điệu làm nản lòng suy nghĩ này.
atlex2

15
Chết tiệt, anh ta đoán người lãnh đạo nút thứ hai. Đó là cách mà ngành công nghiệp tiến bộ. Nếu những kẻ nút không đoán được các nhà lãnh đạo đã đưa ra các mô hình tương tranh dựa trên luồng, thì chúng ta sẽ không có nút.
d512

20

Có một cái nhìn vào nút-rfr .

Nó đơn giản như thế này:

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

tôi nghĩ rằng dòng thứ hai nên là var myModule = rfr ('/ projectSubDir / myModule');
Sikorski

1
Từ các tài liệu: var module2 = rfr ('lib / module2'); // Dấu gạch chéo hàng đầu có thể được bỏ qua.
igelineau 19/03/2015

Tôi đã thử nó và rfr hoạt động tốt khi thực thi với nút, nhưng nó phá vỡ điều hướng mã bằng Mã VS ... Tôi chưa thể tìm ra cách giải quyết, để có thể sử dụng tự động hoàn thành trong VS ...
Alex Mantaut

13

Nếu bạn đang sử dụng sợi thay vì npm, bạn có thể sử dụng không gian làm việc .

Giả sử tôi có một thư mục servicestôi muốn yêu cầu dễ dàng hơn:

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

Để tạo không gian làm việc Sợi, hãy tạo một package.jsontệp bên trong services folder:

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

Trong gói chính.json của bạn thêm:

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

Chạy yarn installtừ gốc của dự án.

Sau đó, bất cứ nơi nào trong mã của bạn, bạn có thể làm:

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

thay vì một cái gì đó như:

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

6
Có lẽ đó là một ý tưởng để làm rõ rằng điều này chỉ hoạt động cho sợi , không phải cho npm? Tôi cho rằng nó có thể cũng hoạt động trong npm, vì vậy đã dành một chút thời gian để tự hỏi tôi đã làm gì sai cho đến khi tôi thử sử dụng sợi thay thế. Có thể là một giả định ngu ngốc, nhưng có lẽ tôi không phải là người duy nhất.
ArneHugo

2
Tôi đã chỉnh sửa một chút để làm rõ. Xin lỗi vì sự nhầm lẫn.
cyberwombat

12

IMHO, cách dễ nhất là xác định chức năng của riêng bạn như là một phần của GLOBALđối tượng. Tạo projRequire.jstrong thư mục gốc của dự án của bạn với các nội dung sau:

var projectDir = __dirname;

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

Trong tệp chính của bạn trước khi nhập requirebất kỳ mô-đun cụ thể nào của dự án:

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

Sau đó làm việc cho tôi:

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

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


@Totty, tôi đã đưa ra một giải pháp khác, có thể hoạt động cho trường hợp bạn mô tả trong các bình luận. Mô tả sẽ được tl;dr, vì vậy tôi tốt hơn nên hiển thị một hình ảnh với cấu trúc của dự án thử nghiệm của tôi .


tốt, cho đến bây giờ đây có vẻ là cách tốt nhất để làm điều đó. Tôi làm: GLOBAL.requires = Yêu cầu ('r'). R; trong tệp index.js của tôi. Nhưng tôi có một vấn đề trong các bài kiểm tra nguyện của mình, họ không chạy index.js nên các bài kiểm tra của tôi không thành công vì yêu cầu không xác định được. Dù sao, bây giờ tôi có thể thêm GLOBAL.requires = quiries ('r'). R; ở đầu mỗi bài kiểm tra. còn ý tưởng nào tốt hơn không? github.com/totty90/production01_server/commit/ từ
Totty.js


vấn đề xảy ra khi tôi ở trong "pathes-test / node_modules / other.js" và tôi yêu cầu "pathes-test / node_modules / some.js". Tôi nên yêu cầu ('./ some') thay vì yêu cầu ("prj / some"). Và theo cách này, tất cả ứng dụng của tôi sẽ nằm trong thư mục node_modules?
Totty.js

@Totty, không có vấn đề yêu cầu prj/sometừ prj/other(chỉ cần thử nghiệm require('prj/some'). Tất cả các mô-đun phổ biến của ứng dụng của bạn có thể đi đến đó (ví dụ: lớp cơ sở dữ liệu). Sẽ không có sự khác biệt nơi bạn, giả sử, liblà. Hãy thử và xem nó có phù hợp không.
Aleksei Zabrodskii

yest, tôi đã cập nhật nó: github.com/totty90/production01_server/tree/master/node_modules/ cảm thấy rất hiệu quả. Nhưng tôi có thể đặt tất cả các tệp của mình lên một cấp mà không cần sử dụng node_modules không?
Totty.js

12

Tôi sử dụng process.cwd()trong các dự án của tôi. Ví dụ:

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

Có thể đáng lưu ý rằng điều này sẽ dẫn đến requiremột con đường tuyệt đối, mặc dù tôi vẫn chưa gặp vấn đề với điều này.


1
Đó là ý tưởng tồi vì CWD không phải là cùng một thư mục nơi ứng dụng được lưu.
jiwopene

11

Có một cuộc thảo luận tốt về vấn đề này ở đây .

Tôi gặp vấn đề kiến ​​trúc tương tự: muốn có một cách để ứng dụng của tôi có nhiều không gian tên tổ chức và nội bộ hơn, mà không có:

  • trộn các mô-đun ứng dụng với các phụ thuộc bên ngoài hoặc làm phiền với các repo npm riêng cho mã dành riêng cho ứng dụng
  • sử dụng tương đối đòi hỏi, làm cho tái cấu trúc và hiểu khó hơn
  • sử dụng liên kết tượng trưng hoặc thay đổi đường dẫn nút, có thể che khuất các vị trí nguồn và không chơi độc đáo với kiểm soát nguồn

Cuối cùng, tôi quyết định tổ chức mã của mình bằng cách sử dụng các quy ước đặt tên tệp thay vì các thư mục. Một cấu trúc sẽ trông giống như:

  • npm-shrwrap.json
  • pack.json
  • nút_modules
    • ...
  • src
    • app.js
    • app.config.js
    • app.models.bar.js
    • app.models.foo.js
    • app.web.js
    • app.web.routes.js
    • ...

Sau đó, trong mã:

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

hoặc chỉ

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

và các phụ thuộc bên ngoài có sẵn từ node_modules như bình thường:

var express = require('express');

Theo cách này, tất cả các mã ứng dụng được sắp xếp theo thứ bậc thành các mô-đun và có sẵn cho tất cả các mã khác liên quan đến gốc ứng dụng.

Tất nhiên, nhược điểm chính là trong trình duyệt tệp, bạn không thể mở rộng / thu gọn cây như thể nó thực sự được tổ chức thành các thư mục. Nhưng tôi thích điều đó rất rõ ràng về việc tất cả các mã đến từ đâu và nó không sử dụng bất kỳ 'ma thuật' nào.


Từ ý chính mà bạn đã liên kết, giải pháp số 7, "The Wrapper", khá đơn giản và tiện lợi.
Cầu tàu-Luc Gendreau

Tôi thấy thêm một chút tiện lợi - "di chuyển" một tệp vào "thư mục" khác trở thành đổi tên - dễ dàng hơn so với di chuyển tệp. Thêm vào đó tôi có xu hướng nhận thấy rằng sau nửa giờ làm việc trong dự án, gần như tất cả cây ứng dụng của tôi đều được mở rộng. Thêm 1 cấp không gian thư mục có thể giúp quản lý cơ sở mã lớn và không giới thiệu quá nhiều ../x/xthứ đã có thể đọc được.
Trượt tuyết

Bạn đang phát minh lại các thư mục, sử dụng dấu chấm thay vì dấu gạch chéo, để khắc phục sự thiếu rõ ràng trong nodejs.
Simone Gianni

9

Giả sử root dự án của bạn là thư mục làm việc hiện tại, cái này sẽ hoạt động:

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

// require file relative to current working directory
config = require( path.resolve('.','config.js') );

config = require('./config.js');cũng hợp lệ
cespon 17/05/2015

7
@cespon không có gì chỉ liên quan đến tập tin yêu cầu.
protometa 21/07/2015

8

Tôi đã thử nhiều giải pháp này. Cuối cùng tôi đã thêm phần này vào đầu tệp chính của mình (ví dụ: index.js):

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

Điều này thêm gốc dự án vào NODE_PATH khi tập lệnh được tải. Việc này cho phép tôi yêu cầu bất kỳ tệp nào trong dự án của mình bằng cách tham chiếu đường dẫn tương đối của nó từ gốc dự án, chẳng hạn như var User = require('models/user'). Giải pháp này sẽ hoạt động miễn là bạn đang chạy một tập lệnh chính trong thư mục gốc của dự án trước khi chạy bất cứ thứ gì khác trong dự án của bạn.


8

Một số câu trả lời nói rằng cách tốt nhất là thêm mã vào node_module dưới dạng một gói, tôi đồng ý và có lẽ đó là cách tốt nhất để mất ../../../yêu cầu nhưng không ai trong số họ thực sự đưa ra cách làm như vậy.

từ phiên bản, 2.0.0bạn có thể cài đặt một gói từ các tệp cục bộ, có nghĩa là bạn có thể tạo thư mục trong thư mục gốc của mình với tất cả các gói bạn muốn,

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

vì vậy, trong gói.json, bạn có thể thêm modules(hoặc foobar) dưới dạng gói mà không cần xuất bản hoặc sử dụng máy chủ bên ngoài như thế này:

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

Sau đó npm install, bạn có thể truy cập mã var foo = require("foo"), giống như bạn làm với tất cả các gói khác.

Thông tin thêm có thể được tìm thấy ở đây :

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

và đây là cách tạo một gói:

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


1
"Tính năng này hữu ích cho phát triển ngoại tuyến cục bộ và tạo các thử nghiệm yêu cầu cài đặt npm khi bạn không muốn truy cập máy chủ bên ngoài, nhưng không nên được sử dụng khi xuất bản các gói lên sổ đăng ký công khai."
Ryan Smith

7

Bạn có thể sử dụng một mô-đun tôi đã thực hiện, Undot . Nó không có gì tiên tiến, chỉ là một người trợ giúp để bạn có thể tránh những dấu chấm địa ngục một cách đơn giản.

Thí dụ:

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

6

Bạn có thể định nghĩa một cái gì đó như thế này trong app.js của bạn:

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

và sau đó bất cứ lúc nào bạn muốn yêu cầu một cái gì đó từ gốc, bất kể bạn đang ở đâu, bạn chỉ cần sử dụng requestFromRoot thay vì vanilla yêu cầu. Hoạt động khá tốt cho tôi cho đến nay.


Cảm ơn! Tôi nghĩ rằng điều này là khá thông minh và đơn giản.
Ryan

Hãy tha thứ cho cha, vì con đã phạm tội. Tôi đã chuyển cái này sang ES6 và nhận được những điều sau đây : requireFromRoot = ((root) => (resource) => require(`${root}/${resource}`))(__dirname);. Yêu giải pháp, nhưng bạn có thực sự phải ràng buộc __dirname như thế không?
Nuck

1
Bộ nhớ của tôi hơi mơ hồ về điều này, nhưng tôi tin rằng __dirname thay đổi giá trị tùy thuộc vào tập tin mà nó được sử dụng trong đó. Bây giờ có thể là do hàm được xác định ở một nơi duy nhất nhưng được sử dụng ở nhiều nơi, nên giá trị sẽ không đổi ngay cả khi không có ràng buộc này, nhưng tôi chỉ làm vậy để đảm bảo rằng đây thực tế là trường hợp.
dùng1417684

đã làm điều này một thời gian dài trước đây, gây ra đau đớn trong việc thử nghiệm envs và tương tự. không có giá trị trên không. toàn cầu mới ngẫu nhiên làm cho những người mới không chắc chắn bla bla
The Dembinski

Và làm thế nào để bạn có requirechức năng này?
Darko Maksimovic

5

Đây là cách thực tế tôi đang làm trong hơn 6 tháng. Tôi sử dụng một thư mục có tên là node_modules làm thư mục gốc của tôi trong dự án, theo cách này, nó sẽ luôn tìm thư mục đó từ mọi nơi tôi gọi là một yêu cầu tuyệt đối:

  • nút_modules
    • dự án của tôi
      • index.js Tôi có thể yêu cầu ("myProject / someFolder / hey.js") thay vì yêu cầu ("./ someFolder / hey.js")
      • someFolder có chứa hey.js

Điều này hữu ích hơn khi bạn được lồng vào các thư mục và việc thay đổi vị trí tệp sẽ được thực hiện theo cách tuyệt đối. Tôi chỉ sử dụng 2 yêu cầu tương đối trong toàn bộ ứng dụng của mình .


4
Tôi sử dụng cách tiếp cận tương tự, ngoại trừ việc tôi thêm địa phương (dự án) node_modulesvào /srcvà để lại /node_modulescho các nhà cung cấp để giữ mọi thứ riêng biệt. Vì vậy, tôi có /src/node_modulesmã địa phương và /node_modulescho các nhà cung cấp.
Marius Balčytis

33
IMHO thư mục node_modules chỉ dành cho node_modules. Nó không phải là một thực hành tốt để đặt toàn bộ dự án của bạn trong thư mục đó.
McSas

2
@McSas bạn muốn đề xuất gì thay thế để có được hiệu quả tương tự như trên?
spieglio

3
@cspiegl Bạn có thể sử dụng NODE_PATHbiến môi trường
Christopher Tarquini

5

Imho cách dễ nhất để đạt được điều này là bằng cách tạo một liên kết tượng trưng khi khởi động ứng dụng tại node_modules/app(hoặc bất cứ điều gì bạn gọi nó) mà trỏ đến ../app. Sau đó, bạn có thể chỉ cần gọi require("app/my/module"). Liên kết tượng trưng có sẵn trên tất cả các nền tảng chính.

Tuy nhiên, bạn vẫn nên chia nội dung của mình thành các mô-đun nhỏ hơn, có thể bảo trì được cài đặt qua npm. Bạn cũng có thể cài đặt các mô-đun riêng thông qua git-url, vì vậy không có lý do gì để có một thư mục ứng dụng nguyên khối.


Hỗ trợ trên Windows đòi hỏi kiến ​​thức chuyên sâu hơn về Node và HĐH. Nó có thể hạn chế việc sử dụng rộng rãi một dự án nguồn mở.
Steven Vachon

Nói chung, tôi sẽ không sử dụng mẫu này cho một thư viện (mà hầu hết các dự án nguồn mở). Tuy nhiên, có thể tạo các liên kết tượng trưng này trong hook build npm để người dùng không có kiến ​​thức chuyên sâu.
Julian Ewald

Chắc chắn, nhưng Node.js trên Windows không hỗ trợ symlink theo mặc định.
Steven Vachon

4

Trong dự án của riêng bạn, bạn có thể sửa đổi bất kỳ tệp .js nào được sử dụng trong thư mục gốc và thêm đường dẫn của nó vào một thuộc tính của process.envbiến. Ví dụ:

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

Sau đó, bạn có thể truy cập vào tài sản ở khắp mọi nơi:

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

4

Một câu trả lời khác:

Hãy tưởng tượng cấu trúc thư mục này:

  • nút_modules
    • nhà nghỉ
  • src
    • subir
      • foo.js
      • bar.js
    • main.js
  • kiểm tra

    • test.js

Sau đó, trong test.js , bạn cần yêu cầu các tệp như thế này:

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

và trong tệp main.js :

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

Bây giờ bạn có thể sử dụng babeltrình giải quyết mô-đun babel-plugin-mô-đun này. tập tin babelrc để cấu hình 2 thư mục gốc:

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

Bây giờ bạn có thể yêu cầu các tệp theo cách tương tự trong các thử nghiệm và trong src :

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

và nếu bạn muốn sử dụng cú pháp mô-đun es6 :

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

sau đó bạn nhập tệp trong các bài kiểm trasrc như thế này:

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


3

Không thể examplesthư mục chứa một node_modulesliên kết tượng trưng đến thư mục gốc của dự án, project -> ../../do đó cho phép các ví dụ sử dụng require('project'), mặc dù điều này không xóa ánh xạ, nhưng nó cho phép nguồn sử dụng require('project')thay vì require('../../').

Tôi đã thử nghiệm điều này và nó hoạt động với v0.6.18.

Danh sách projectthư mục:

$ 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 -> ../../

Nội dung của việc index.jsgán một giá trị cho một thuộc tính của exportsđối tượng và gọi console.logvới một thông báo cho biết nó được yêu cầu. Nội dung của test.jsrequire('project').


bạn có thể hiển thị mã nguồn của bài kiểm tra của bạn xin vui lòng? tốt, và nó sẽ hoạt động nếu tôi phải yêu cầu ('project.a') theo cách này?
Totty.js

Bạn có ý nghĩa require('project.a')gì? Tôi nghĩ rằng điều đó có nghĩa là require('project/a'), mặc dù require('project').acũng có thể?
Dan D.

nhưng với ví dụ của bạn, tôi sẽ cần tạo các thư mục đó trong mỗi thư mục có mô-đun cần phương thức yêu cầu. Dù sao, bạn sẽ cần phải quan tâm đến thời gian của "../" tùy thuộc vào thư mục.
Totty.js

Trên thực tế, liên kết sẽ chỉ cần nằm trong một node_modulesthư mục trong phần cha mẹ gần nhất của cả hai tệp và liên kết sau đó sẽ giống nhau cho cả hai. Xem nodejs.org/api/ Kẻ
Dan D.

Và sẽ được tương đối từ vị trí đó. Ví dụ : project/node_modules/project -> ../.
Dan D.

2

Nếu bất cứ ai đang tìm kiếm một cách khác để khắc phục vấn đề này, đây là đóng góp của riêng tôi cho nỗ lực:

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

Ý tưởng cơ bản: bạn tạo một tệp JSON trong thư mục gốc của dự án ánh xạ các filepath của bạn thành các tên tốc ký (hoặc sử dụng trình tự động sử dụng để làm điều đó cho bạn). Sau đó, bạn có thể yêu cầu các tệp / mô-đun của mình bằng cách sử dụng các tên đó. Thích như vậy:

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

Vì vậy, có đó.


2

Những gì tôi muốn làm là tận dụng cách tải nút từ thư mục node_module cho việc này.

Nếu một người cố tải "mô-đun", người ta sẽ làm một cái gì đó như

require('thing');

Node sau đó sẽ tìm thư mục 'thing' trong thư mục 'node_module'.

Vì node_module thường là gốc của dự án, chúng ta có thể tận dụng tính nhất quán này. (Nếu node_module không ở gốc, thì bạn phải tự giải quyết những cơn đau đầu khác.)

Nếu chúng ta đi vào thư mục và sau đó thoát ra khỏi nó, chúng ta có thể nhận được một đường dẫn nhất quán đến thư mục gốc của dự án nút.

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

Sau đó, nếu chúng ta muốn truy cập vào thư mục / happy, chúng ta sẽ làm điều này.

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

Mặc dù nó có một chút hacky, tuy nhiên tôi cảm thấy nếu chức năng của cách tải node_modules thay đổi, sẽ có những vấn đề lớn hơn để giải quyết. Hành vi này nên được nhất quán.

Để làm cho mọi thứ rõ ràng, tôi làm điều này, vì tên của mô-đun không quan trọng.

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

Tôi đã sử dụng nó gần đây cho angular2. Tôi muốn tải một dịch vụ từ thư mục gốc.

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

Về tham chiếu Angular của bạn, với ứng dụng CLI tiêu chuẩn, bạn chỉ cần nhập src/app/my.service, bạn cũng có thể định cấu hình VSC để sử dụng nhập không liên quan cho các tệp bản thảo.
Ploppy

2

Tôi đã viết gói nhỏ này cho phép bạn yêu cầu các gói theo đường dẫn tương đối của chúng từ gốc dự án, mà không đưa ra bất kỳ biến toàn cục hoặc ghi đè mặc định nút nào

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

Nó hoạt động như thế này

// 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()

Đôi khi tôi có các gói riêng trong dự án chính, kịch bản này sẽ phá vỡ điều đó. Ngoài ra, tôi không chắc chắn sẽ hoạt động tốt với webpack (trong trường hợp bạn sử dụng webpack với node.js như tôi)
Totty.js

Nếu bạn có các thư mục lồng nhau với các tệp gói, mỗi thư mục sẽ chỉ có thể yêu cầu các tệp trong gói của nó. Đó không phải là hành vi bạn muốn sao? Tôi chưa thử nghiệm với webpack.
gafi

Điều này làm việc hoàn hảo cho một dự án đơn giản và dễ dàng hơn nhiều so với bất kỳ câu trả lời nào khác.
byxor

2

Chỉ muốn theo dõi câu trả lời tuyệt vời từ Paolo Moretti và Browserify. Nếu bạn đang sử dụng một bộ chuyển mã (ví dụ: babel, bản thảo) và bạn có các thư mục riêng cho mã nguồn và mã được mã hóa như src/dist/, bạn có thể sử dụng một biến thể của các giải pháp như

nút_modules

Với cấu trúc thư mục sau:

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

sau đó bạn có thể để babel vv để chuyển srcthư mục vào distthư mục.

liên kết tượng trưng

Sử dụng symlink chúng ta có thể thoát khỏi một số cấp độ lồng nhau:

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

Một báo trước với babel --copy-file Các --copy-fileslá cờ của babelkhông đối phó với liên kết tượng trưng tốt. Nó có thể tiếp tục điều hướng vào ..symlink và nhìn thấy các tập tin vô tận. Cách giải quyết là sử dụng cấu trúc thư mục sau:

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

Theo cách này, mã bên dưới srcvẫn sẽ appđược giải quyết src, trong khi babel sẽ không thấy các liên kết tượng trưng nữa.


Cảm ơn, nhưng tôi không khuyên bạn nên làm điều kỳ diệu này. Trước tiên, bạn sẽ mất tất cả các lần nhập, chúng sẽ không được IDE của bạn tính toán. Nếu bạn sử dụng các công cụ khác như kiểu dòng chảy, nó sẽ không hoạt động chính xác.
Totty.js

Trên thực tế, luồng có vẻ hoạt động trong trường hợp của tôi, điều này không đáng ngạc nhiên vì các giải pháp phụ thuộc vào mô hình phân giải mô đun nút tiêu chuẩn và các liên kết tượng trưng. Vì vậy, nó không thực sự kỳ diệu cho các công cụ như dòng chảy để hiểu. Nhưng IDE thì khác.
dùng716468

2

Tôi đang tìm kiếm sự đơn giản chính xác để yêu cầu các tệp từ bất kỳ cấp độ nào và tôi đã tìm thấy bí danh mô-đun .

Chỉ cần cài đặt:

npm i --save module-alias

Mở tệp pack.json của bạn, tại đây bạn có thể thêm bí danh cho các đường dẫn của mình, ví dụ:

"_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
}

Và sử dụng bí danh của bạn bằng cách đơn giản:

require('module-alias/register')
const deep = require('@deep')
const module = require('something')


1

Chúng tôi sắp thử một cách mới để giải quyết vấn đề này.

Lấy ví dụ từ các dự án đã biết khác như mùa xuân và guice, chúng tôi sẽ xác định một đối tượng "bối cảnh" sẽ chứa tất cả các câu lệnh "yêu cầu".

Đối tượng này sau đó sẽ được chuyển đến tất cả các mô-đun khác để sử dụng.

Ví dụ

var context = {}

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

Điều này đòi hỏi chúng ta phải viết mỗi mô-đun như là một hàm nhận opts, xem chúng là một cách thực hành tốt nhất ..

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

và sau đó bạn sẽ đề cập đến bối cảnh thay vì yêu cầu công cụ.

var module1Ref = bối cảnh.moduel1;

Nếu bạn muốn, bạn có thể dễ dàng viết một vòng lặp để thực hiện các câu lệnh yêu cầu

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

Điều này sẽ làm cho cuộc sống dễ dàng hơn khi bạn muốn chế giễu (kiểm tra) và cũng giải quyết vấn đề của bạn trên đường đi trong khi làm cho mã của bạn có thể tái sử dụng như một gói.

Bạn cũng có thể sử dụng lại mã khởi tạo ngữ cảnh bằng cách tách khai báo bean khỏi nó. ví dụ: main.jstập tin của bạn có thể trông như vậy

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

Phương pháp này cũng áp dụng cho các thư viện bên ngoài, không cần mã cứng tên của họ mỗi khi chúng tôi yêu cầu - tuy nhiên nó sẽ yêu cầu xử lý đặc biệt vì xuất khẩu của họ không phải là chức năng mong đợi bối cảnh ..

Sau này, chúng ta cũng có thể định nghĩa các bean là các hàm - sẽ cho phép chúng ta requirecác mô-đun khác nhau tùy theo môi trường - nhưng nó nằm ngoài phạm vi của luồng này.


1

Tôi đã gặp rắc rối với vấn đề tương tự, vì vậy tôi đã viết một gói được gọi là bao gồm .

Bao gồm các thẻ điều khiển tìm ra thư mục gốc của dự án của bạn bằng cách định vị tệp pack.json của bạn, sau đó chuyển đối số đường dẫn mà bạn đưa ra cho yêu cầu gốc () mà không gặp phải tất cả các đường dẫn tương đối. Tôi tưởng tượng điều này không phải là sự thay thế cho request (), mà là một công cụ để yêu cầu xử lý các tệp hoặc thư viện không đóng gói / không phải của bên thứ ba. Cái gì đó như

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

Tôi hy vọng điều này có thể hữu ích.


1

Nếu tệp js điểm vào ứng dụng của bạn (tức là tệp bạn thực sự chạy "nút" trên) nằm trong thư mục gốc của dự án, bạn có thể thực hiện việc này thực sự dễ dàng với mô đun npm rootpath . Đơn giản chỉ cần cài đặt nó qua

npm install --save rootpath

... sau đó ở trên cùng của tệp js điểm vào, thêm:

require('rootpath')();

Từ thời điểm đó trở đi, tất cả các cuộc gọi yêu cầu bây giờ đều liên quan đến root dự án - vd require('../../../config/debugging/log'); trở thành require('config/debugging/log');(nơi thư mục cấu hình nằm trong thư mục gốc của dự án).


1

Trong các dòng đơn giản, bạn có thể gọi thư mục của riêng bạn dưới dạng mô-đun:

Cho rằng chúng ta cần: mô-đun đường dẫn toàn cầu và mô-đun ứng dụng

ở đây "Đường dẫn mô-đun ứng dụng" là mô-đun, nó cho phép bạn thêm các thư mục bổ sung vào đường dẫn tìm kiếm mô-đun Node.js Và "toàn cầu" là, mọi thứ bạn đính kèm vào đối tượng này sẽ có sẵn ở mọi nơi trong ứng dụng của bạn.

Bây giờ hãy xem đoạn trích này:

global.appBasePath = __dirname;

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

__dirname là thư mục đang chạy hiện tại của nút. Bạn có thể đưa đường dẫn của riêng mình vào đây để tìm đường dẫn cho mô-đun.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.