如何使用Express / Node.JS创建可在所有视图中访问的全局变量?


80

好的,所以我使用Jekyll建立了一个博客,您可以在文件中定义变量_config.yml,所有模板/布局均可访问该变量。我目前正在将Node.JS / ExpressEJS模板和ejs-locals一起使用(用于局部/布局。我正在寻找与全局变量类似的方法site.title_config.yml如果有人熟悉Jekyll ,就会发现这些变量。我有像网站的标题(而不是页面标题),作者/公司名称,在我所有页面上都保持不变。

这是我目前正在做的一个例子:

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
    });
};

我希望能够在一个地方定义诸如网站标题,描述,作者等之类的变量,并通过EJS在我的布局/模板中对其进行访问,而不必将其作为选项传递给每次调用res.render。有没有办法做到这一点,并且仍然允许我传递每个页面专用的其他变量?

Answers:


108

在有机会学习Express 3 API Reference之后,我发现了我想要的东西。具体来说,关于条目的内容app.locals,然后再往下一点,res.locals保存了我需要的答案。

我自己发现该函数app.locals接受一个对象并将其所有属性存储为作用于应用程序的全局变量。这些全局变量作为局部变量传递给每个视图。res.locals但是,该函数的作用域是请求,因此,响应局部变量仅可用于在特定请求/响应过程中呈现的视图。

所以对于我来说,我app.js所做的就是添加:

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'
    }
});

然后,所有这些变量都在我的意见,访问site.titlesite.descriptionauthor.nameauthor.contact

我还可以使用定义每个请求响应的局部变量res.locals,或者简单地将诸如页面标题之类的变量作为调用中的options参数传递render

编辑:此方法将不会让你在你的中间件使用这些本地人。我确实确实碰到了这一点,正如Pickels在下面的评论中建议的那样。在这种情况下,您将需要在他的替代(和赞赏的)答案中创建一个中间件功能。您的中间件功能需要res.locals为每个响应将它们添加到中,然后调用next。该中间件功能将需要放在需要使用这些本地语言的任何其他中间件之上。

编辑:通过app.locals和声明局部变量之间的另一个区别res.locals是,app.locals变量被设置为单个时间,并在应用程序的整个生命周期中持续存在。res.locals在中间件中设置本地人时,每次收到请求时都会进行设置。基本上,您应该更喜欢通过设置全局变量,app.locals除非该值取决于req传递给中间件的请求变量。如果该值未更改,则只需将其设置一次将更加有效app.locals


1
app.locals比我建议的要有意义。它有一个问题,您不能访问中间件中的本地人。在这种情况下,这可能并不重要,但这只是您有时会遇到的陷阱之一。
Pickels

您还可以在js或json文件中定义配置,并在需要的任何地方简单地require()即可。该文件在应用程序期间仅加载一次,并且每个需要该文件的模块都可以访问其定义的值。请参阅stackoverflow.com/questions/5869216/…上
Joe Lapp 2014年

51
Express 4 locals中,不是函数,而是对象。设置属性:app.locals.site.title = 'Example';
马丁·莱恩

哇!现在,整个应用程序可通过单个文件进行配置。干净的镜头。+1
迪帕克

@MarttiLaine谢谢朋友!
阿里·埃姆雷

54

您可以通过将它们添加到常规中间件中的locals对象中来实现。

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也是一个函数,它将扩展locals对象而不是覆盖它。因此以下内容也适用

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

完整的例子

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'));

我还添加了通用渲染中间件。这样,您不必在每个路由中添加res.render,这意味着您可以更好地重用代码。一旦您走上了可重用的中间件路线,您会发现您将拥有许多构建模块,这些构建模块将极大地加快开发速度。


我知道这是一个古老的答案,但是您的globalLocals函数中的req和的参数res是向后的。
brandon927 2015年

我是否可以通过这种方式查询旨在成为全局数据的数据,例如,每个页面请求都直接从db加载的navbar数据?这似乎是这个想法可能会超越本地人的使用,例如猫鼬呼叫。我正在寻找每条路径上的直接负载,因为navbar是全局的,以后会研究缓存。或者,也许我需要运行一次的函数,然后在收集数据后将其加载到本地。
blamb

1
res.locals似乎不再是一个功能。
煞风景

我用您的方法将一些产品展示到页脚中。谢谢!
卡纳鲁·瓦伦丁

18

对于Express 4.0,我发现使用应用程序级变量的工作方式略有不同,而Cory的答案对我而言却行不通。

从文档中:http : //expressjs.com/en/api.html#app.locals

我发现您可以在中为应用程序声明一个全局变量

app.locals

例如

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

然后在您的应用程序中,您可以访问这些变量,而在您的表达中间件中,您可以在req对象中访问它们,如下所示:

req.app.locals.baseUrl

例如

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

1
另外,在代码中添加新的本地语言时,还需要重新启动Express服务器。
Wtower '16

14

在您的app.js中,您需要添加如下内容

global.myvar = 100;

现在,在您要使用此变量的所有文件中,您可以按以下方式访问它: myvar


我正在使用require模块,并且不得不在global.myvar任何地方引用它,并且有效。对于socket.io来说,这是一个很好的简单解决方案,在这里我想从其他文件的处理程序中发出消息。我放上“ io”对象global.myIO,一切都很好。
汤姆·波特

6
实际上,不建议这样做,也不认为这是Node开发中的好习惯。Node.JS包括一个基于CommonJS系统的模块系统实现,并且完全以不共享全局范围的模块的思想为中心,而是使用require方法。这也会在Node中设置一个全局js var,而不是像问题所要求的那样在Express视图/模板中设置一个本地js var。
Cory Gross

@CoryGross将请求对象设置为像这样的全局变量是一种好习惯吗?
阿里·谢拉菲特

仅此有效。谢谢F先生!@CoryGross如何让应用程序中的所有文件访问单个对象?
Divij Sehgal

这是最好的答案。这意味着可以将全局对象传递给视图或所需的任何位置。这样global.cdnURL = "https://cdn.domain.com,您可以传递到视图以加载静态内容。

1

一种方法是通过在app.locals该应用中更新该应用程序的变量app.js

通过以下设置

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

获取/访问

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

使用@RamRovi示例的屏幕截图进行了细微的增强。

在此处输入图片说明


1

您还可以使用“全局”

例:

像这样声明:

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

这样使用:在任何视图或ejs文件中<%console.log(site_url); %>

在js文件中console.log(site_url);


0

有了不同的答案,我实现了此代码,以使用在“ app.locals”中加载的外部文件JSON

参量

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

应用

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;

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>

希望它会有所帮助


-1

为了避免全局范围被污染,我要做的是创建一个可以包含在任何地方的脚本。

// 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;

这样做只会创建一次新实例,并在包含文件的任何地方共享该实例。我不确定是因为变量被缓存还是因为应用程序的内部引用机制(可能包括缓存)而导致的。关于节点如何解决此问题的任何评论都很棒。

也许还请阅读以下内容以了解需求如何工作的要点:http : //fredkschott.com/post/2014/06/require-and-the-module-system/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.