Node.js / Express.js - App.router hoạt động như thế nào?


298

Trước khi tôi hỏi về app.routertôi nghĩ tôi nên giải thích ít nhất những gì tôi nghĩ sẽ xảy ra khi làm việc với phần mềm trung gian. Để sử dụng phần mềm trung gian, chức năng sử dụng là app.use(). Khi phần mềm trung gian đang được thực thi, nó sẽ gọi phần mềm trung gian tiếp theo bằng cách sử dụng next()hoặc làm cho nó để không có thêm phần mềm trung gian nào được gọi. Điều đó có nghĩa là thứ tự mà tôi thực hiện các cuộc gọi phần mềm trung gian của mình rất quan trọng, bởi vì một số phần mềm trung gian phụ thuộc vào phần mềm trung gian khác và một số phần mềm trung gian gần cuối thậm chí có thể không được gọi.

Hôm nay tôi đã làm việc trên ứng dụng của mình và máy chủ của tôi đang chạy ẩn. Tôi muốn thực hiện một số thay đổi và làm mới trang của mình và xem các thay đổi ngay lập tức. Cụ thể, tôi đã thay đổi bố cục của mình. Tôi không thể làm cho nó hoạt động được vì vậy tôi đã tìm kiếm Stack Overflow để tìm câu trả lời và tìm thấy câu hỏi này . Nó nói để chắc chắn rằng express.static()bên dưới require('stylus'). Nhưng khi tôi nhìn vào mã của OP, tôi thấy rằng anh ta có app.routercuộc gọi của anh ta ở cuối cuộc gọi phần mềm trung gian của anh ta, và tôi đã cố gắng tìm hiểu tại sao lại như vậy.

Khi tôi tạo ứng dụng Express.js của mình (phiên bản 3.0.0rc4), tôi đã sử dụng lệnh express app --sessions --css stylusvà trong tệp app.js của mình, mã được thiết lập với app.routercả trên express.static()require('stylus')các cuộc gọi của tôi. Vì vậy, có vẻ như, nếu nó đã được thiết lập theo cách đó, thì nó sẽ giữ nguyên như vậy.

Sau khi sắp xếp lại mã của tôi để tôi có thể thấy bút stylus của mình thay đổi, nó trông như thế này:

app.configure(function(){
  //app.set() calls
  //app.use() calls
  //...
  app.use(app.router);
  app.use(require('stylus').middleware(__dirname + '/public'));
  app.use(express.static(__dirname + '/public', {maxAge: 31557600000}));
});

app.get('/', routes.index);

app.get('/test', function(req, res){
  res.send('Test');
});

Vì vậy, tôi quyết định rằng bước đầu tiên sẽ là tìm hiểu tại sao điều quan trọng là thậm chí phải có app.routertrong mã của tôi. Vì vậy, tôi nhận xét nó, bắt đầu ứng dụng của tôi và điều hướng đến /. Nó hiển thị trang chỉ mục của tôi tốt. Hmm, có lẽ nó hoạt động được vì tôi đã xuất định tuyến từ tệp tuyến đường của mình (Rout.index). Vì vậy, tiếp theo tôi điều hướng đến /testvà nó hiển thị Kiểm tra trên màn hình. Haha, OK, tôi không có ý tưởng gì app.routerkhông. Cho dù nó có được bao gồm trong mã của tôi hay không, định tuyến của tôi vẫn ổn. Vì vậy, tôi chắc chắn đang thiếu một cái gì đó.

Vì vậy, đây là câu hỏi của tôi:

Ai đó có thể vui lòng giải thích những gì app.routerkhông, tầm quan trọng của nó và nơi tôi nên đặt nó trong các cuộc gọi phần mềm trung gian của tôi? Nó cũng sẽ tốt nếu tôi có một lời giải thích ngắn gọn về express.static(). Theo như tôi có thể nói, express.static()là một bộ đệm thông tin của tôi và nếu ứng dụng không thể tìm thấy trang được yêu cầu, nó sẽ kiểm tra bộ đệm để xem nó có tồn tại không.


18
Cảm ơn bạn đã hỏi câu hỏi này. Tôi đã đi vòng quanh để tìm câu trả lời này (và câu hỏi để nhắc nó).
Hari Seldon

8
Đó là một câu hỏi thực sự được viết tốt, tôi đã googling điều tương tự.
Kirn

Câu trả lời:


329

Lưu ý: Phần này mô tả cách Express hoạt động trong phiên bản 2 và 3. Xem phần cuối của bài đăng này để biết thông tin về Express 4.


staticchỉ cần phục vụ các tệp ( tài nguyên tĩnh ) từ đĩa. Bạn cung cấp cho nó một đường dẫn (đôi khi được gọi là điểm gắn kết) và nó phục vụ các tệp trong thư mục đó.

Ví dụ, express.static('/var/www')sẽ phục vụ các tệp trong thư mục đó. Vì vậy, một yêu cầu đến máy chủ Node của bạn http://server/file.htmlsẽ phục vụ/var/www/file.html .

routerlà mã chạy các tuyến đường của bạn. Khi bạn làm app.get('/user', function(req, res) { ... });, đó làrouter thực sự gọi hàm gọi lại để xử lý yêu cầu.

Thứ tự mà bạn chuyển mọi thứ để app.usexác định thứ tự mà mỗi phần mềm trung gian được cung cấp cơ hội để xử lý một yêu cầu. Ví dụ: nếu bạn có một tệp được gọi test.htmltrong thư mục tĩnh và tuyến đường:

app.get('/test.html', function(req, res) {
    res.send('Hello from route handler');
});

Cái nào được gửi cho khách hàng yêu cầu http://server/test.html? Bất cứ phần mềm trung gian nào được trao chouse đầu tiên.

Nếu bạn làm điều này:

app.use(express.static(__dirname + '/public'));
app.use(app.router);

Sau đó các tập tin trên đĩa được phục vụ.

Nếu bạn làm theo cách khác,

app.use(app.router);
app.use(express.static(__dirname + '/public'));

Sau đó, trình xử lý tuyến đường nhận được yêu cầu và "Xin chào từ trình xử lý tuyến đường" được gửi đến trình duyệt.

Thông thường, bạn muốn đặt bộ định tuyến ở trên phần mềm trung gian tĩnh để một tệp vô tình có tên không thể ghi đè lên một trong các tuyến của bạn.

Lưu ý rằng nếu bạn không rõ ràng usesự router, nó được ngầm bổ sung bởi Express tại điểm bạn xác định một lộ trình (đó là lý do các tuyến đường của bạn vẫn làm việc ngay cả khi bạn nhận xét ra app.use(app.router)).


Một nhà bình luận đã đưa ra một quan điểm khác về thứ tự staticrouter tôi đã không đề cập: ảnh hưởng đến hiệu suất tổng thể của ứng dụng của bạn.

Một lý do khác use routerở trên staticlà để tối ưu hóa hiệu suất. Nếu bạn đặt staticđầu tiên, thì bạn sẽ nhấn vào ổ cứng trên mỗi yêu cầu để xem liệu có tồn tại một tệp hay không. Trong một thử nghiệm nhanh , tôi thấy rằng chi phí này lên tới ~ 1ms trên một máy chủ không tải. (Con số đó nhiều khả năng sẽ cao hơn khi tải, trong đó các yêu cầu sẽ cạnh tranh để truy cập đĩa.)

Với routerlần đầu tiên, một yêu cầu khớp với một tuyến đường không bao giờ phải đánh vào đĩa, tiết kiệm mili giây quý giá.

Tất nhiên, có nhiều cách để giảm thiểu staticchi phí.

Tùy chọn tốt nhất là đặt tất cả tài nguyên tĩnh của bạn vào một thư mục cụ thể. (IE /static) Sau đó, bạn có thể gắn kết staticvới đường dẫn đó để nó chỉ chạy khi đường dẫn bắt đầu bằng /static:

app.use('/static', express.static(__dirname + '/static'));

Trong tình huống này, bạn sẽ đặt nó ở trên router . Điều này tránh việc xử lý phần mềm trung gian / bộ định tuyến khác nếu có tệp, nhưng thành thật mà nói, tôi nghi ngờ bạn sẽ thu được nhiều như vậy.

Bạn cũng có thể sử dụng staticCache, lưu trữ tài nguyên tĩnh trong bộ nhớ để bạn không phải nhấn vào đĩa cho các tệp thường được yêu cầu. ( Cảnh báo: staticCache rõ ràng sẽ bị xóa trong tương lai.)

Tuy nhiên, tôi không nghĩ staticCachelưu trữ các câu trả lời phủ định (khi tệp không tồn tại), vì vậy sẽ không hữu ích nếu bạn đặt staticCacheở trên routermà không gắn nó vào đường dẫn.

Như với tất cả các câu hỏi về hiệu suất, đo lường và điểm chuẩn ứng dụng trong thế giới thực của bạn (đang tải) để xem các nút thắt thực sự ở đâu.


Thể hiện 4

Express 4.0 loại bỏ app.router . Tất cả phần mềm trung gian ( app.use) và tuyến đường (app.get et al) hiện được xử lý chính xác theo thứ tự mà chúng được thêm vào.

Nói cách khác:

Tất cả các phương thức định tuyến sẽ được thêm vào theo thứ tự xuất hiện. Bạn không nên làm app.use(app.router). Điều này giúp loại bỏ vấn đề phổ biến nhất với Express.

Nói cách khác, trộn app.use()app[VERB]()sẽ hoạt động chính xác theo thứ tự mà chúng được gọi.

app.get('/', home);
app.use('/public', require('st')(process.cwd()));
app.get('/users', users.list);
app.post('/users', users.create);

Đọc thêm về những thay đổi trong Express 4.


2
Việc routerđi ở một nơi. Nếu lần đầu tiên bạn gọi app.get(hoặc posthoặc những người khác), bạn chưa used app.router, Express cho biết thêm điều đó cho bạn.
josh3736

4
@MikeCauser: Không, vì chi phí truy cập đĩa (để xem có tồn tại một tệp hay không) lớn hơn chi phí gọi hàm. Trong thử nghiệm của tôi , chi phí đó lên tới 1ms trên một máy chủ không tải. Điều đó rất có thể sẽ cao hơn khi tải, nơi các yêu cầu sẽ cạnh tranh để truy cập đĩa. Sau staticđó router, câu hỏi về phần mềm trung gian khác trở nên không liên quan vì nó phải ở trên bộ định tuyến.
josh3736

2
Giải thích tuyệt vời! Cảm ơn bạn rất nhiều!
Kirn

3
app.routerđược loại bỏ trong nhánh chính hiện tại, sẽ là express-4.0 . Mỗi tuyến trở thành một phần mềm trung gian riêng biệt.
yanyar

3
Thêm một điều rõ ràng nữa, khi tôi làm việc với điều này. Trong express 4, nhiều tuyến đường có thể được gán cho bộ định tuyến và sau đó để sử dụng bộ định tuyến, bộ định tuyến được cung cấp một đường dẫn gốc và được đặt trong ngăn xếp "phần mềm trung gian" thông qua app.use (đường dẫn, bộ định tuyến). Điều này cho phép các tuyến liên quan đến từng sử dụng bộ định tuyến của riêng mình và được chỉ định một đường dẫn cơ sở dưới dạng một đơn vị. Nếu tôi hiểu nó tốt hơn, tôi sẽ đề nghị đăng một câu trả lời khác. Một lần nữa, tôi nhận được điều này từ scotch.io/tutorials/javascript/ Kẻ
Joe Lapp

2

Định tuyến có nghĩa là xác định cách ứng dụng đáp ứng yêu cầu của khách hàng đến một điểm cuối cụ thể, đó là URI (hoặc đường dẫn) và phương thức yêu cầu HTTP cụ thể (GET, POST, v.v.). Mỗi tuyến có thể có một hoặc nhiều chức năng xử lý, được thực thi khi tuyến được khớp.

Trong Bộ định tuyến Express 4.0, chúng tôi được linh hoạt hơn bao giờ hết trong việc xác định các tuyến đường của chúng tôi.

express.Router () được sử dụng nhiều lần để xác định các nhóm tuyến.

tuyến đường được sử dụng như phần mềm trung gian để xử lý các yêu cầu.

tuyến đường được sử dụng làm phần mềm trung gian để xác thực các tham số bằng cách sử dụng ".param ()".

app.route () được sử dụng làm lối tắt đến Bộ định tuyến để xác định nhiều yêu cầu trên một tuyến đường

khi chúng tôi đang sử dụng app.route (), chúng tôi sẽ đính kèm ứng dụng của mình với bộ định tuyến đó.

var express = require('express'); //used as middleware
var app = express(); //instance of express.
app.use(app.router);
app.use(express.static(__dirname + '/public')); //All Static like [css,js,images] files are coming from public folder
app.set('views',__dirname + '/views'); //To set Views
app.set('view engine', 'ejs'); //sets View-Engine as ejs
app.engine('html', require('ejs').renderFile); //actually rendering HTML files through EJS. 
app.get('/', function (req, res) {
  res.render('index');  
})
app.get('/test', function (req, res) {
  res.send('test')
})

0

Trong phiên bản tốc hành 4, chúng ta có thể dễ dàng xác định các tuyến đường theo cách sau:

server.js:

const express = require('express');
const app = express();
const route = require('./route');

app.use('/route', route);
// here we pass in the imported route object

app.listen(3000, () => console.log('Example app listening on port 3000!'));

route.js:

const express = require('express');
const router = express.Router();

router.get('/specialRoute', function (req, res, next) {
     // route is now http://localhost:3000/route/specialRoute
});

router.get('/', function (req, res, next) {
    // route is now http://localhost:3000/route
});

module.exports = router;

Trong server.jschúng tôi đã nhập đối tượng bộ định tuyến của route.jstệp và áp dụng nó theo cách sau server.js:

app.use('/route', route);

Bây giờ tất cả các tuyến đường trong route.jscó URL cơ sở sau:

http: // localhost: 3000 / tuyến

Tại sao cách tiếp cận này:

Ưu điểm chính của cách tiếp cận này là bây giờ ứng dụng của chúng tôi có nhiều mô-đun hơn . Bây giờ tất cả các trình xử lý tuyến đường cho một tuyến nhất định có thể được đưa vào các tệp khác nhau giúp mọi thứ dễ bảo trì hơn và dễ tìm hơn.

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.