Làm thế nào để tạo các biến toàn cục có thể truy cập được trong tất cả các chế độ xem bằng Express / Node.JS?


81

Ok, vậy là tôi đã xây dựng một blog bằng Jekyll và bạn có thể xác định các biến trong một tệp _config.ymlcó thể truy cập được trong tất cả các mẫu / bố cục. Tôi hiện đang sử dụng Node.JS / Express với các mẫu EJSejs-local (cho các phân đoạn / bố cục. Tôi đang tìm cách thực hiện điều gì đó tương tự với các biến toàn cục giống như site.titleđược tìm thấy trong _config.ymlnếu bất kỳ ai quen thuộc với Jekyll. Tôi có các biến như tiêu đề trang web, (thay vì tiêu đề trang), tác giả / tên công ty, giữ nguyên trên tất cả các trang của tôi.

Đây là một ví dụ về những gì tôi hiện đang làm:

exports.index = function(req, res){
    res.render('index', { 
        siteTitle: 'My Website Title',
        pageTitle: 'The Root Splash Page',
        author: 'Cory Gross',
        description: 'My app description',
        indexSpecificData: someData
    });
};

exports.home = function (req, res) {
    res.render('home', {
        siteTitle: 'My Website Title',
        pageTitle: 'The Home Page',
        author: 'Cory Gross',
        description: 'My app description',
        homeSpecificData: someOtherData
    });
};

Tôi muốn có thể xác định các biến như tiêu đề, mô tả, tác giả, v.v. của trang web của mình ở một nơi và có thể truy cập chúng trong bố cục / mẫu của tôi thông qua EJS mà không cần phải chuyển chúng dưới dạng tùy chọn cho mỗi lần gọi tới res.render. Có cách nào để làm điều này và vẫn cho phép tôi chuyển các biến khác cụ thể cho từng trang không?

Câu trả lời:


109

Sau khi có cơ hội nghiên cứu Tài liệu tham khảo API Express 3 thêm một chút, tôi đã khám phá ra những gì tôi đang tìm kiếm. Cụ thể là các mục cho app.localsvà sau đó xa hơn một chút res.localsgiữ các câu trả lời tôi cần.

Tôi đã tự mình khám phá ra rằng hàm app.localsnhận một đối tượng và lưu trữ tất cả các thuộc tính của nó dưới dạng các biến toàn cục trong phạm vi ứng dụng. Các hình cầu này được chuyển dưới dạng các biến cục bộ cho mỗi chế độ xem. Chức năng res.locals, tuy nhiên, có phạm vi để yêu cầu và do đó, các biến phản ứng cục bộ có thể truy cập chỉ với view (s) được dựng trong đó yêu cầu đặc biệt / phản ứng.

Vì vậy, đối với trường hợp của tôi, app.jsnhững gì tôi đã làm là thêm:

app.locals({
    site: {
        title: 'ExpressBootstrapEJS',
        description: 'A boilerplate for a simple web application with a Node.JS and Express backend, with an EJS template with using Twitter Bootstrap.'
    },
    author: {
        name: 'Cory Gross',
        contact: 'CoryG89@gmail.com'
    }
});

Sau đó, tất cả các biến có thể truy cập trong quan điểm của tôi là site.title, site.description, author.name, author.contact.

Tôi cũng có thể xác định các biến cục bộ cho mỗi phản hồi đối với một yêu cầu với res.localshoặc chỉ cần chuyển các biến như tiêu đề của trang vào làm optionstham số trong lệnh rendergọi.

CHỈNH SỬA: Phương pháp này sẽ không cho phép bạn sử dụng những địa phương này trong phần mềm trung gian của bạn. Tôi thực sự đã gặp phải điều này như Pickels gợi ý trong bình luận bên dưới. Trong trường hợp này, bạn sẽ cần tạo một hàm middleware như vậy trong câu trả lời thay thế (và được đánh giá cao) của anh ấy. Chức năng phần mềm trung gian của bạn sẽ cần phải thêm chúng vào res.localscho mỗi phản hồi và sau đó gọi next. Chức năng phần mềm trung gian này sẽ cần phải được đặt trên bất kỳ phần mềm trung gian nào khác cần sử dụng các local này.

CHỈNH SỬA: Một sự khác biệt khác giữa khai báo local thông qua app.localsres.localsvới app.localscác biến được đặt một lần duy nhất và tồn tại trong suốt vòng đời của ứng dụng. Khi bạn đặt local với res.localsphần mềm trung gian của mình, chúng sẽ được thiết lập mỗi khi bạn nhận được yêu cầu. Về cơ bản, bạn nên đặt toàn cầu thông qua app.localstrừ khi giá trị phụ thuộc vào reqbiến yêu cầu được chuyển vào phần mềm trung gian. Nếu giá trị không thay đổi thì sẽ hiệu quả hơn nếu chỉ đặt một lần vào app.locals.


1
app.locals có ý nghĩa hơn những gì tôi đã đề xuất. Tuy nhiên, nó có một điểm khó khăn là bạn không thể truy cập vào các địa phương trong phần mềm trung gian của mình. Trong trường hợp này, nó có thể không quan trọng nhưng đó là một trong những vấn đề mà đôi khi bạn gặp phải.
Đón khách vào

Bạn cũng có thể xác định cấu hình trong tệp js hoặc json và chỉ cần yêu cầu () nó ở mọi nơi bạn cần. Tệp sẽ chỉ tải một lần trong suốt ứng dụng và mọi mô-đun yêu cầu nó đều có quyền truy cập vào các giá trị mà nó đã xác định. Xem các câu trả lời khác nhau tại stackoverflow.com/questions/5869216/…
Joe Lapp,

51
Trong Express 4 locals không phải là một hàm, mà là một đối tượng. Để đặt tài sản:app.locals.site.title = 'Example';
Martti Laine

Chà! Giờ đây, toàn bộ ứng dụng có thể được định cấu hình thông qua một tệp duy nhất. Một cú đánh sạch. +1
Dipak

@MarttiLaine Cảm ơn bạn thân!
Ali Emre Çakmakoğlu

55

Bạn có thể làm điều này bằng cách thêm chúng vào đối tượng local trong một phần mềm trung gian chung.

app.use(function (req, res, next) {
   res.locals = {
     siteTitle: "My Website's Title",
     pageTitle: "The Home Page",
     author: "Cory Gross",
     description: "My app's description",
   };
   next();
});

Locals cũng là một hàm sẽ mở rộng đối tượng local hơn là ghi đè lên nó. Vì vậy, những điều sau đây cũng hoạt động

res.locals({
  siteTitle: "My Website's Title",
  pageTitle: "The Home Page",
  author: "Cory Gross",
  description: "My app's description",
});

Đầy đủ ví dụ

var app = express();

var middleware = {

    render: function (view) {
        return function (req, res, next) {
            res.render(view);
        }
    },

    globalLocals: function (req, res, next) {
        res.locals({ 
            siteTitle: "My Website's Title",
            pageTitle: "The Root Splash Page",
            author: "Cory Gross",
            description: "My app's description",
        });
        next();
    },

    index: function (req, res, next) {
        res.locals({
            indexSpecificData: someData
        });
        next();
    }

};


app.use(middleware.globalLocals);
app.get('/', middleware.index, middleware.render('home'));
app.get('/products', middleware.products, middleware.render('products'));

Tôi cũng đã thêm một phần mềm trung gian kết xuất chung. Bằng cách này, bạn không phải thêm res.render vào mỗi tuyến đường, có nghĩa là bạn có khả năng tái sử dụng mã tốt hơn. Khi bạn đi xuống lộ trình phần mềm trung gian có thể tái sử dụng, bạn sẽ nhận thấy rằng bạn sẽ có rất nhiều khối xây dựng sẽ tăng tốc độ phát triển lên rất nhiều.


Tôi biết đây là một câu trả lời cũ, nhưng các tham số trong globalLocalschức năng của bạn cho reqresngược lại.
brandon927,

Tôi có thể truy vấn dữ liệu dự định là toàn cầu, theo cách này, ví dụ: dữ liệu thanh điều hướng tải trực tiếp từ db từng yêu cầu trang không? Đó dường như là ý tưởng này có thể vượt quá cách sử dụng của người dân địa phương, ví dụ như một cuộc gọi cầy mangut. Tôi đang tìm kiếm tải trực tiếp, trên mọi tuyến đường, vì thanh điều hướng là toàn cầu, sẽ xem xét bộ nhớ đệm sau. Ngoài ra, có thể tôi cần một chức năng chạy một lần, sau đó tải chúng cho người dân địa phương sau khi thu thập dữ liệu.
blamb

1
res.localsdường như không còn là một chức năng nữa.
killjoy

Tôi sử dụng cách tiếp cận của bạn để hiển thị một số sản phẩm vào chân trang. Cảm ơn bạn!
Carnaru Valentin

18

Đối với Express 4.0, tôi thấy rằng việc sử dụng các biến cấp ứng dụng hoạt động hơi khác một chút và câu trả lời của Cory không phù hợp với tôi.

Từ tài liệu: http://expressjs.com/en/api.html#app.locals

Tôi thấy rằng bạn có thể khai báo một biến toàn cục cho ứng dụng trong

app.locals

ví dụ

app.locals.baseUrl = "http://www.google.com"

Và sau đó trong ứng dụng của bạn, bạn có thể truy cập các biến này và trong phần mềm trung gian express của bạn, bạn có thể truy cập chúng trong đối tượng req như

req.app.locals.baseUrl

ví dụ

console.log(req.app.locals.baseUrl)
//prints out http://www.google.com

1
Ngoài ra, máy chủ express cần được khởi động lại khi các địa phương mới được thêm vào mã.
Wtower

14

Trong app.js của bạn, bạn cần thêm một cái gì đó như thế này

global.myvar = 100;

Bây giờ, trong tất cả các tệp bạn muốn sử dụng biến này, bạn chỉ có thể truy cập nó với tư cách myvar


Tôi đang sử dụng các mô-đun yêu cầu và phải tham chiếu nó global.myvarở mọi nơi, và điều đó đã hoạt động. Đây là một giải pháp dễ dàng tốt đẹp cho socket.io, nơi tôi muốn gửi thông báo từ các trình xử lý trong các tệp khác. Tôi đã đặt đối tượng "io" của mình global.myIOvà tất cả đều hoạt động tốt.
Thom Porter

6
Điều này thực sự không được khuyến khích hoặc được coi là một thực hành tốt trong phát triển Node. Node.JS bao gồm việc triển khai hệ thống mô-đun dựa trên hệ thống CommonJS và hoàn toàn tập trung vào ý tưởng về các mô-đun không chia sẻ phạm vi toàn cầu, thay vào đó sử dụng phương thức request. Điều này cũng đặt var js toàn cục trong Node thay vì cục bộ cho chế độ xem / mẫu Express như câu hỏi yêu cầu.
Cory Gross

@CoryGross Có phải là một phương pháp hay để đặt đối tượng yêu cầu làm biến toàn cục như thế này không?
Ali Sherafat,

Chỉ điều này hoạt động. Cảm ơn anh F! @CoryGross làm cách nào để cho phép tất cả các tệp trong ứng dụng của tôi truy cập vào một đối tượng?
Divij Sehgal

Đây là câu trả lời tốt nhất. Nó có nghĩa là đối tượng toàn cục có sẵn để chuyển đến một chế độ xem hoặc bất cứ nơi nào bạn muốn. Như global.cdnURL = "https://cdn.domain.comvậy, bạn có thể chuyển đến một khung nhìn để tải lên hình nón tĩnh.
tập một

1

Một cách để thực hiện việc này bằng cách cập nhật app.localsbiến cho ứng dụng đó trongapp.js

Đặt qua sau

var app = express();
app.locals.appName = "DRC on FHIR";

Nhận / Truy cập

app.listen(3000, function () {
    console.log('[' + app.locals.appName + '] => app listening on port 3001!');
});

Hoàn thiện với ảnh chụp màn hình từ ví dụ @RamRovi với một chút cải tiến.

nhập mô tả hình ảnh ở đây


1

bạn cũng có thể sử dụng "toàn cầu"

Thí dụ:

khai báo như thế này:

  app.use(function(req,res,next){
      global.site_url = req.headers.host;   // hostname = 'localhost:8080'
      next();
   });

Sử dụng như thế này: trong bất kỳ chế độ xem hoặc tệp ejs nào <% console.log (site_url); %>

trong tệp js console.log (site_url);


0

Với các câu trả lời khác nhau, tôi đã triển khai mã này để sử dụng tệp bên ngoài JSON được tải trong "app.locals"

Thông số

{
    "web": {
        "title" : "Le titre de ma Page",
        "cssFile" : "20200608_1018.css"
    }
}

Ứng dụng

var express     = require('express');
var appli       = express();
var serveur     = require('http').Server(appli);

var myParams    = require('./include/my_params.json');
var myFonctions = require('./include/my_fonctions.js');

appli.locals = myParams;

Trang EJS

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title><%= web.title %></title>
    <link rel="stylesheet" type="text/css" href="/css/<%= web.cssFile %>">
</head>

</body>
</html>

Hy vọng nó sẽ giúp


-1

Những gì tôi làm để tránh phạm vi toàn cầu bị ô nhiễm là tạo một tập lệnh mà tôi có thể đưa vào bất cứ đâu.

// my-script.js
const ActionsOverTime = require('@bigteam/node-aot').ActionsOverTime;
const config = require('../../config/config').actionsOverTime;
let aotInstance;

(function () {
  if (!aotInstance) {
    console.log('Create new aot instance');
    aotInstance = ActionsOverTime.createActionOverTimeEmitter(config);
  }
})();

exports = aotInstance;

Làm điều này sẽ chỉ tạo một phiên bản mới một lần và chia sẻ nó ở mọi nơi nơi tệp được bao gồm. Tôi không chắc đó là do biến được lưu trong bộ đệm hay do cơ chế tham chiếu nội bộ cho ứng dụng (có thể bao gồm bộ nhớ đệm). Mọi nhận xét về cách nút giải quyết vấn đề này sẽ rất tuyệt.

Cũng có thể đọc phần này để biết ý chính về cách hoạt động của Request : http://fredkschott.com/post/2014/06/require-and-the-module-system/

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.