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

omjgkv6w  于 2023-08-04  发布在  Node.js
关注(0)|答案(8)|浏览(100)

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

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。有没有一种方法可以做到这一点,并仍然允许我在其他变量特定于每个页面?

ufj5ltwl

ufj5ltwl1#

Express 4/5+版本更新:

处理这个问题的最佳解决方案仍然是使用app.locals,如API参考中所述。正如这里的一些评论所指出的,API在Express 4中进行了更新,使得app.locals不再是一个函数,而现在只是一个对象。所以如果你试图把它作为一个函数调用,你会得到一个错误。
要在Express 4/5+中使用app.locals来完成与我下面的Express 3原始示例相同的事情,您可以在应用程序中可以访问Express app对象的任何地方设置app.locals

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访问。

**注:**或者,您也可以单独设置app.locals上的值。

例如,这也可以工作:

app.locals.authorName = 'Cory Gross';
app.locals.authorContact = 'coryg89@gmail.com';


但是,在默认情况下,这将 * 不**工作,并会抛出错误:

app.locals.author.name = 'Cory Gross';
app.locals.author.contact = 'coryg89@gmail.com';


这是因为,虽然app.locals已经作为Express公开的对象存在,您可以在其上定义属性,但app.locals.author还不存在,因此上面的代码将抛出一个错误,抱怨您试图在未定义的对象上设置属性。为了做到这一点,你首先必须将app.locals.author = {}设置为空对象,或者同时设置app.localsapp.locals.author下的所有内容,类似于我上面的第一个例子。

中间件使用

尽管app.locals上设置的值在所有视图中都可以全局访问,但在Express 3中使用它的潜在问题之一是,默认情况下,这些值不一定可以在中间件函数中访问。由于这个限制,一个替代方案,如最初在Pickel's answer中建议的那样,是在顶级中间件中设置res.locals上的值,以便这些值在所有较低的中间件以及所有视图中都可用。然而,这种替代方案的缺点是,设置所有这些值的逻辑将为每个请求/响应反复执行,而不是像app.locals那样设置一次并持续到服务器运行。
在Express 4/5+中,如果您希望在app.locals上设置的值在中间件函数和所有视图中可用,现在有一个更好的解决方案。为了解决这个问题,Express 4/5+ API现在在所有中间件函数中公开Express app对象,可以作为req参数的属性req.app访问。因此,默认情况下,您现在可以轻松地访问所有中间件函数中在app.locals上设置的所有全局应用程序范围的值,只需在中间件中以req.app.locals的身份访问该对象即可。

Express第三版原始答案:

在有机会对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为请求的每个响应定义局部变量,或者简单地将页面标题之类的变量作为render调用中的options参数传递。

**EDIT:**此方法将 * 不 * 允许您在中间件中使用这些局部变量。实际上,正如皮克尔斯在下面的评论中所建议的那样,我确实遇到了这个问题。在这种情况下,您将需要创建一个中间件功能,就像他的替代(和赞赏)答案一样。您的中间件函数需要为每个响应将它们添加到res.locals,然后调用next。这个中间件函数需要放在需要使用这些局部变量的任何其他中间件之上。
**编辑:**通过app.localsres.locals声明局部变量的另一个区别是,使用app.locals时,变量只设置一次,并在应用程序的整个生命周期中持续存在。当您在中间件中使用res.locals设置局部变量时,每次收到请求时都会设置这些变量。基本上,您应该更喜欢通过app.locals设置全局变量,除非该值取决于传入中间件的请求req变量。如果该值没有改变,那么在app.locals中只设置一次会更有效。

o0lyfsai

o0lyfsai2#

您可以通过将它们添加到通用中间件中的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也是一个扩展局部变量对象而不是覆盖它的函数。所以下面的也可以

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

z9ju0rcb

z9ju0rcb3#

对于Express4.0,我发现使用应用程序级变量的工作方式略有不同& Cory的答案对我不起作用。
从文档:http://expressjs.com/en/api.html#app.locals
我发现可以在

app.locals

字符串
例如

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


然后在应用程序中可以访问这些变量&在Express中间件中可以在Req对象中作为

req.app.locals.baseUrl


例如,在

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

ki1q1bka

ki1q1bka4#

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

global.myvar = 100;

字符串
现在,在您想要使用此变量的所有文件中,您可以仅以myvar的形式访问它

ppcbkaq5

ppcbkaq55#

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

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

字符串
获取/访问

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


详细说明来自@RamRovi示例的屏幕截图,略有增强。
x1c 0d1x的数据

0dxa2lsx

0dxa2lsx6#

你也可以使用“全局”
示例如下:
这样宣布:

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

gorkyyrv

gorkyyrv7#

有了不同的答案,我实现了这段代码来使用加载在“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>


希望能有所帮助

lg40wkob

lg40wkob8#

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

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

相关问题