Hiểu các mô-đun Node.js: nhiều yêu cầu trả về cùng một đối tượng?


106

Tôi có một câu hỏi liên quan đến tài liệu node.js về bộ nhớ đệm mô-đun :

Các mô-đun được lưu vào bộ nhớ đệm sau lần đầu tiên chúng được tải. Điều này có nghĩa là (trong số những thứ khác) rằng mọi lệnh gọi yêu cầu ('foo') sẽ nhận được chính xác cùng một đối tượng được trả về, nếu nó phân giải thành cùng một tệp.

Nhiều lệnh gọi yêu cầu ('foo') có thể không khiến mã mô-đun được thực thi nhiều lần. Đây là một tính năng quan trọng. Với nó, các đối tượng "đã hoàn thành một phần" có thể được trả về, do đó cho phép tải các phụ thuộc bắc cầu ngay cả khi chúng gây ra chu kỳ.

Có nghĩa là gì với may?

Tôi muốn biết nếu request sẽ luôn trả về cùng một đối tượng. Vì vậy, trong trường hợp tôi yêu cầu mô-đun A trong app.jsvà thay đổi đối tượng xuất khẩu bên trong app.js( đối tượng yêu cầu trả về) và sau đó yêu cầu mô-đun B trong app.jsđó bản thân mô-đun A yêu cầu mô-đun A , tôi sẽ luôn nhận được phiên bản sửa đổi của đối tượng đó hay một?

// app.js

var a = require('./a');
a.b = 2;
console.log(a.b); //2

var b = require('./b');
console.log(b.b); //2

// a.js

exports.a = 1;

// b.js

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

7
Câu đó trong tài liệu có thể được viết tốt hơn. Đối với tôi, vẻ như không giống như không được phép , tức là, nhiều lệnh gọi yêu cầu ('foo') không thể khiến mã mô-đun được thực thi nhiều lần .
Lucio Paiva

@LucioPaiva Đã tạo một PR để sửa nó: github.com/nodejs/node/pull/23143
mikemaccana 28/09/18

Họ có nghĩa là [[có thể] không gây ra], trái ngược với [[có thể] không gây ra], cùng nghĩa với "Không, Timmy, bạn có thể không có thêm sô cô la nào nữa". Vì vậy, trong bối cảnh này, tôi đồng ý, điều đó được viết một cách mơ hồ.
Ciabaros

Câu trả lời:


76

Nếu cả hai app.jsb.jsnằm trong cùng một dự án (và trong cùng một thư mục) thì cả hai sẽ nhận được cùng một phiên bản của A. Từ tài liệu node.js :

... mọi lệnh gọi tới require('foo')sẽ nhận được trả về chính xác cùng một đối tượng , nếu nó phân giải thành cùng một tệp .


Tình hình là khác nhau khi a.js, b.jsapp.jsđang trong module NPM khác nhau . Ví dụ:

[APP] --> [A], [B]
[B]   --> [A]

Trong trường hợp đó, require('a')trong app.jssẽ giải quyết cho một bản sao khác nhau của a.jshơn require('a')trong b.jsvà do đó trả về một ví dụ khác nhau của A. Có một bài đăng trên blog mô tả hành vi này chi tiết hơn.


1
"và trong cùng một thư mục". Tôi nhận được trường hợp tương tự khi [B] ở trong thư mục con của nơi [Ứng dụng] sống.
Bill Tarbell

1
@BillTarbell Khi tôi muốn lấy cùng một phiên bản từ các thư mục khác nhau, tôi có hai cái khác nhau.
MustSeeMelons

1
Lời cảnh báo cuối cùng này có còn đúng bây giờ rằng npm sẽ cài đặt A, B và APP trong cấu trúc thư mục phẳng không? Nếu không, làm thế nào một mô-đun có thể được thiết lập để lưu kết quả vào bộ nhớ cache giữa nhiều mô-đun khác?
Michael

Từ tài liệu node.js: "Các mô-đun được lưu vào bộ nhớ đệm dựa trên tên tệp đã phân giải của chúng. Vì các mô-đun có thể phân giải thành một tên tệp khác dựa trên vị trí của mô-đun gọi (tải từ các thư mục node_modules), nên nó không phải là đảm bảo yêu cầu ('foo ') sẽ luôn trả về cùng một đối tượng, nếu nó phân giải thành các tệp khác nhau. " Vì vậy, bạn không thể sử dụng bộ nhớ cache yêu cầu để thực hiện hành vi singleton, hệ thống tệp không phân biệt chữ hoa chữ thường hoặc liên kết tượng trưng và điều đó có thể khiến cùng một mô-đun được tải nhiều lần. Nếu bạn cần dữ liệu toàn cục, bạn cần sử dụng đối tượng "toàn cầu".
Tannin

6

node.js có một số loại bộ nhớ đệm được triển khai để chặn nút đọc tệp hàng 1000 lần trong khi thực hiện một số dự án máy chủ lớn.

Bộ nhớ cache này được liệt kê trong require.cacheđối tượng. Tôi phải lưu ý rằng đối tượng này có thể đọc / ghi, mang lại khả năng xóa tệp khỏi bộ đệm mà không làm chết quá trình.

http://nodejs.org/docs/latest/api/globals.html#require.cache

Ouh, quên trả lời câu hỏi. Việc sửa đổi đối tượng đã xuất không ảnh hưởng đến việc tải mô-đun tiếp theo. Điều này sẽ gây ra nhiều rắc rối ... Yêu cầu luôn trả về một phiên bản mới của đối tượng, không có tham chiếu. Chỉnh sửa tệp và xóa bộ nhớ cache không thay đổi đối tượng đã xuất

Sau khi thực hiện một số thử nghiệm, node.js sẽ lưu vào bộ nhớ cache của module.exports. Việc sửa đổi require.cache[{module}].exportskết thúc bằng một đối tượng trả về mới, được sửa đổi.


1
Mã tôi đã đăng thực sự hoạt động. b.bđược xác định với giá trị 2. Vì vậy, trong ví dụ này, nó là cùng một đối tượng.
Xomby

Đó là một tính năng và trong mắt tôi khá hữu ích. Câu hỏi là nếu tôi có thể phụ thuộc vào nó. Tài liệu cho biết mayđiều đó làm cho nó không rõ ràng.
Xomby

Việc thực thi một hàm theo request trong a.js không thay đổi bất cứ điều gì ... Nó tiếp tục lưu vào bộ nhớ đệm đối tượng được trả về. Thật kỳ lạ, ứng dụng của tôi hoạt động như thế nào? Cách duy nhất là, như được mô tả trong tài liệu, thực thi một hàm được truyền sau khi yêu cầu.
moe

1
đôi khi các đối tượng giống nhau. Đừng dựa vào đó, hãy chuyển một phiên bản mô-đun đơn lẻ nếu bạn cần.
Ricardo Tomasi

có cách nào để lấy lại một đối tượng hoàn toàn mới theo yêu cầu (không phải đối tượng được lưu trong bộ nhớ cache) không?
Matt

3

Kể từ khi câu hỏi được đăng, tài liệu đã được cập nhật để làm rõ lý do tại sao "may" ban đầu được sử dụng. Bây giờ nó tự trả lời câu hỏi bằng cách làm cho mọi thứ rõ ràng (tôi nhấn mạnh là hiển thị những gì đã thay đổi):

Các mô-đun được lưu vào bộ nhớ đệm sau lần đầu tiên chúng được tải. Điều này có nghĩa là (trong số những thứ khác) rằng mọi lệnh gọi yêu cầu ('foo') sẽ nhận được chính xác cùng một đối tượng được trả về, nếu nó phân giải thành cùng một tệp.

Yêu cầu đã cung cấp không được sửa đổi, nhiều lệnh gọi yêu cầu ('foo') sẽ không làm cho mã mô-đun được thực thi nhiều lần. Đây là một tính năng quan trọng. Với nó, các đối tượng "đã hoàn thành một phần" có thể được trả về, do đó cho phép tải các phụ thuộc bắc cầu ngay cả khi chúng gây ra chu kỳ.


2

Đối với những gì tôi đã thấy, nếu tên mô-đun phân giải thành tệp được tải trước, mô-đun đã lưu trong bộ nhớ cache sẽ được trả lại, nếu không tệp mới sẽ được tải riêng.

Đó là, bộ nhớ đệm dựa trên tên tệp thực tế được giải quyết. Điều này là do, nói chung, có thể có các phiên bản khác nhau của cùng một gói được cài đặt ở các cấp khác nhau của hệ thống phân cấp tệp và phải được tải tương ứng.

Điều tôi không chắc là có những trường hợp bộ nhớ cache bị mất hiệu lực không nằm trong tầm kiểm soát hoặc nhận thức của lập trình viên, điều này có thể khiến bạn vô tình tải lại cùng một tệp gói nhiều lần.


1

Trong trường hợp lý do tại sao bạn muốn require(x)trả về một đối tượng mới mỗi lần chỉ là vì bạn sửa đổi đối tượng đó trực tiếp - đó là trường hợp mà tôi đã gặp phải - chỉ cần sao chép nó, sửa đổi và chỉ sử dụng bản sao, như thế này:

var a = require('./a');
a = JSON.parse(JSON.stringify(a));

0

thử drex : https://github.com/yuryb/drex

drex đang xem một mô-đun để cập nhật và yêu cầu lại mô-đun đó một cách rõ ràng sau khi cập nhật. Mã mới đang được yêu cầu () d như thể mã mới là một mô-đun hoàn toàn khác, do đó, request.cache không phải là vấn đề.


2
Một câu hỏi khác, nhưng drex trông thực sự tuyệt vời cho các công cụ phát triển. Cảm ơn!
Alex Ivasyuv

0

Khi bạn yêu cầu một đối tượng, bạn đang yêu cầu địa chỉ tham chiếu của nó và bằng cách yêu cầu đối tượng hai lần, bạn sẽ nhận được cùng một địa chỉ! Để có các bản sao của cùng một đối tượng, Bạn nên sao chép (sao chép) nó.

var obj = require('./obj');

a = JSON.parse(JSON.stringify(obj));
b = JSON.parse(JSON.stringify(obj));
c = JSON.parse(JSON.stringify(obj));

Nhân bản được thực hiện theo nhiều cách, bạn có thể xem điều này để biết thêm thông tin.

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.