Sự khác biệt giữa __dirname và ./ trong node.js là gì?


498

Khi lập trình trong Node.js và các tệp tham chiếu được đặt ở đâu đó liên quan đến thư mục hiện tại của bạn, có lý do nào để sử dụng __dirnamebiến thay vì chỉ là thông thường ./không? Tôi đã sử dụng ./ cho đến nay trong mã của tôi và chỉ phát hiện ra sự tồn tại của nó __dirname, và về cơ bản muốn biết liệu có thông minh để chuyển đổi ./ của tôi sang đó hay không, và nếu vậy, tại sao đó lại là một ý tưởng thông minh .


47
tl; dr: Vì vậy, về cơ bản, sự khác biệt là './' và 'process.cwd ()' đề cập đến thư mục hiện tại của thiết bị đầu cuối gọi tập lệnh, trong khi '__dirname' đề cập đến thư mục trong đó tập lệnh là lưu trữ.
Gui Imamura

1
Ngoại trừ khi .được sử dụng bên trong require. Đường dẫn bên trong requireluôn liên quan đến tệp chứa lệnh gọi đến require.
Govind Rai

Câu trả lời:


814

Ý chính

Trong Node.js, __dirnameluôn là thư mục chứa tập lệnh hiện đang thực thi ( xem phần này ). Vì vậy, nếu bạn gõ __dirnamevào /d1/d2/myscript.js, giá trị sẽ là /d1/d2.

Ngược lại, .cung cấp cho bạn thư mục mà bạn đã chạy nodelệnh trong cửa sổ đầu cuối (tức là thư mục làm việc của bạn) khi bạn sử dụng các thư viện như pathfs. Về mặt kỹ thuật, nó bắt đầu như thư mục làm việc của bạn nhưng có thể được thay đổi bằng cách sử dụng process.chdir().

Ngoại lệ là khi bạn sử dụng .với require(). Đường dẫn bên trong requireluôn liên quan đến tệp chứa lệnh gọi đến require.

Ví dụ...

Giả sử cấu trúc thư mục của bạn là

/dir1
  /dir2
    pathtest.js

pathtest.jschứa

var path = require("path");
console.log(". = %s", path.resolve("."));
console.log("__dirname = %s", path.resolve(__dirname));

và bạn làm

cd /dir1/dir2
node pathtest.js

bạn lấy

. = /dir1/dir2
__dirname = /dir1/dir2

Thư mục làm việc của bạn là /dir1/dir2như vậy đó là những gì .giải quyết. Vì pathtest.jsnằm ở /dir1/dir2đó là những gì __dirnamegiải quyết là tốt.

Tuy nhiên, nếu bạn chạy tập lệnh từ /dir1

cd /dir1
node dir2/pathtest.js

bạn lấy

. = /dir1
__dirname = /dir1/dir2

Trong trường hợp đó, thư mục làm việc của bạn là /dir1những gì đã .được giải quyết, nhưng __dirnamevẫn giải quyết /dir1/dir2.

Sử dụng .bên trong require...

Nếu bên trong dir2/pathtest.jsbạn có một requirecuộc gọi bao gồm một tập tin bên trong dir1bạn sẽ luôn luôn làm

require('../thefile')

bởi vì đường dẫn bên trong requireluôn liên quan đến tệp mà bạn đang gọi nó. Nó không có gì để làm với thư mục làm việc của bạn.


38
IMO, lời giải thích này rõ ràng hơn một chút so với câu trả lời được chấp nhận (bạn biết, "thư mục hiện tại" có một chút mơ hồ ở đó).
hóa thân

5
Tôi đồng ý. Tôi sẽ thay đổi câu trả lời được chấp nhận. Xin lưu ý rằng câu trả lời này đã được thêm 2,5 năm sau khi câu trả lời ban đầu được chấp nhận và tôi chỉ nhận thấy nó ngay bây giờ (2 năm sau). :) Muộn còn hơn không!
thisissami

14
Điều đáng chú ý ./là không phải lúc nào thư mục mà nút được khởi chạy. Nó bắt đầu theo cách đó, nhưng có thể được thay đổi thông qua process.chdir(). Vì vậy, ./luôn luôn là thư mục làm việc hiện tại, thường là nút thư mục được khởi chạy từ đó, trừ khi mã của bạn thay đổi rõ ràng thư mục làm việc.
gilly3

3
Tôi hơi bối rối về việc sử dụng. bên trong yêu cầu một phần, nếu đường dẫn bên trong yêu cầu luôn liên quan đến tệp bạn đang gọi, không phải là đường dẫn nên được yêu cầu ('../ thefile') thay vì yêu cầu ('../ dir1 / thefile')? tôi nghĩ rằng .. đưa vị trí hiện tại của đường dẫn trở lại một cấp từ dir2 đến dir1 rồi. Bạn vẫn cần đặt dir1 vào đường dẫn hay tôi bỏ lỡ hiểu điều gì?
andromada

1
Và bạn sẽ làm thế nào nếu bạn cần sử dụng ../someDirtrong một số tập lệnh và bạn sẽ chạy lệnh từ một thư mục khác?
cbdeveloper

154

./đề cập đến thư mục làm việc hiện tại, ngoại trừ trong require()chức năng. Khi sử dụng require(), nó dịch ./sang thư mục của tệp hiện tại được gọi. __dirnameluôn là thư mục của tập tin hiện tại

Ví dụ: với cấu trúc tệp sau

/home/user/dir/files/config.json

{
  "hello": "world"
}

/home/user/dir/files/somefile.txt

text file

/home/user/dir/dir.js

var fs = require('fs');

console.log(require('./files/config.json'));
console.log(fs.readFileSync('./files/somefile.txt', 'utf8'));

Nếu tôi cdvào /home/user/dirvà chạy node dir.jstôi sẽ nhận được

{ hello: 'world' }
text file

Nhưng khi tôi chạy cùng một kịch bản từ /home/user/tôi nhận được

{ hello: 'world' }

Error: ENOENT, no such file or directory './files/somefile.txt'
    at Object.openSync (fs.js:228:18)
    at Object.readFileSync (fs.js:119:15)
    at Object.<anonymous> (/home/user/dir/dir.js:4:16)
    at Module._compile (module.js:432:26)
    at Object..js (module.js:450:10)
    at Module.load (module.js:351:31)
    at Function._load (module.js:310:12)
    at Array.0 (module.js:470:10)
    at EventEmitter._tickCallback (node.js:192:40)

Sử dụng ./làm việc với requirenhưng không cho fs.readFileSync. Đó là bởi vì fs.readFileSync, ./dịch vào cwd (trong trường hợp này /home/user/). Và /home/user/files/somefile.txtkhông tồn tại.


oh tôi nghĩ __dirname là thư mục làm việc hiện tại ... cảm ơn đã làm rõ!
thisissami

Có cách nào để tham chiếu thư mục làm việc của ứng dụng với fs không? Ví dụ: tôi đang cố tải một tệp từ thư mục làm việc /movies, nhưng vì mô-đun của tôi nằm trong một tệp /custom_modules/, __dirnamecố gắng lấy phim từ,/custom_modules/movies

2
Bạn có thể sử dụng ./hoặc process.cwd(). xem nodejs.org/api/ process.html # process_ process_cwd
fent 24/07/14

Đáng lưu ý rằng không nên sử dụng __dirname trên ./ trong các câu lệnh yêu cầu vì mặc dù chúng hoạt động giống hệt nhau trong nút, nhưng nó có thể gây ra sự cố với các bản dựng browserify cho các gói dễ bị tránh.
Jed Watson
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.