84

好的,所以我使用Jekyll构建了一个博客,您可以在文件中定义变量,_config.yml这些变量可在所有模板/布局中访问。我目前正在使用带有EJS模板和ejs -locals的 Node.JS / Express (用于部分/布局。如果有人熟悉 Jekyll,我希望做一些类似于全局变量的事情。我有像网站的标题(而不是页面标题)、作者/公司名称,在我的所有页面上都保持不变。site.title_config.yml

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

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. 有没有办法做到这一点,并且仍然允许我传递特定于每个页面的其他变量?

4

8 回答 8

112

在有机会进一步研究Express 3 API 参考之后,我发现了我正在寻找的东西。具体来说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.title, site.description, author.name, author.contact

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

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

编辑:app.locals通过和声明本地变量的另一个区别res.localsapp.locals变量被设置一次并在应用程序的整个生命周期中持续存在。当您res.locals在中间件中设置本地变量时,每次收到请求时都会设置这些变量。app.locals除非值取决于req传递给中间件的请求变量,否则您基本上应该更喜欢设置全局变量。如果该值没有改变,那么在 中设置一次会更有效app.locals

于 2013-05-11T22:14:31.357 回答
59

您可以通过将它们添加到通用中间件中的 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,这意味着您可以更好地重用代码。一旦你沿着可重用的中间件路线走下去,你会注意到你将拥有大量的构建块,这将极大地加快开发速度。

于 2013-05-09T13:47:39.123 回答
19

对于 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
于 2016-03-18T05:08:17.133 回答
17

在您的 app.js 中,您需要添加类似这样的内容

global.myvar = 100;

现在,在您想要使用此变量的所有文件中,您可以将其访问为myvar

于 2013-09-20T20:46:02.243 回答
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);

于 2016-10-21T08:02:48.427 回答
0

一种方法是更新该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 示例的屏幕截图进行详细说明,并略有增强。

在此处输入图像描述

于 2016-07-16T01:15:42.567 回答
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>

希望它会有所帮助

于 2020-06-10T09:24:15.387 回答
-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;

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

也许还可以阅读本文以了解 require 的工作原理:http: //fredkschott.com/post/2014/06/require-and-the-module-system/

于 2017-07-26T11:46:29.497 回答