在客户端JavaScript中访问Express.js本地变量

70

想知道我是否做得正确,如果不是的话,你们会如何处理。

我有一个Jade模板,需要从MongoDB数据库检索一些数据并且我还需要在客户端JavaScript文件中访问这些数据。

我正在使用Express.js,并将数据发送到Jade模板,如下所示:

var myMongoDbObject = {name : 'stephen'};
res.render('home', { locals: { data : myMongoDbObject } });

那么在 home.jade 中,我可以做这样的事情:

p Hello #{data.name}!

它的输出结果是:

Hello stephen!
现在我想要的是在客户端JS文件中也能访问这个数据对象,这样我就可以在按钮点击之前操作这个对象,然后将其POST回服务器以更新数据库。
通过将"data"对象保存在Jade模板中的隐藏输入字段中,然后在客户端JS文件中获取该字段的值,我已经成功实现了这一点。
在home.jade文件内:
- local_data = JSON.stringify(data) // data coming in from Express.js
input(type='hidden', value=local_data)#myLocalDataObj

那么在我的客户端JS文件中,我可以这样访问local_data:

在myLocalFile.js内部

var localObj = JSON.parse($("#myLocalDataObj").val());
console.log(localObj.name);

但是这个 stringfy/解析的过程感觉很混乱。我知道我可以将数据对象的值绑定到我的 Jade 模板中的 DOM 对象上,然后使用 jQuery 获取这些值,但是我想要在客户端 JS 中访问从 Express 返回的实际对象。

我的解决方案是否最优,你们会如何完成这个任务?


4
几个月前,我实际上创建了一个npm包来解决这个确切的问题:https://github.com/brooklynDev/JShare - BFree
5个回答

74

渲染完成后,只有渲染后的HTML会发送到客户端。因此,没有变量可用了。你可以做的是,不要将对象写入输入元素,而是将对象作为渲染后的JavaScript输出:

script(type='text/javascript').
    var local_data =!{JSON.stringify(data)}

编辑:显然,Jade在第一个闭括号后需要一个点号。


4
根据您使用的视图引擎不同,您可能无法在视图中调用JSON.stringify。相反,您应该在控制器中对其进行字符串化,并将字符串作为变量传递,例如在handlebars中使用{{{jsonData}}} - Tom
提议的解决方案可行,我还建议使用express-expose来更清晰、更整合化地暴露服务器端变量。 - xmikex83
为了安全地在 HTML 中嵌入 JSON,您需要转义一些字符以避免错误/漏洞,详见此答案:https://dev59.com/WG855IYBdhLWcg3wuG-W#4180424。顺便提一下,sharify npm 模块可以正确处理这个问题。 - Peter Lyons
专业提示:只需使用额外的 ' 或 " 将其直接注入为字符串值,例如:var address="!{address}" - Krzysztof Kaczor
@Tom 这种方式是否被 ejs 模板引擎支持? - Frank Fang
显示剩余4条评论

14

我使用稍微不同的方法。在我的控制器中,我这样做:

res.render('search-directory', {
  title: 'My Title',
  place_urls: JSON.stringify(placeUrls),
});

然后在我的jade文件中的javascript中像这样使用它:

var placeUrls = !{place_urls};

在这个例子中,它被用于 Twitter Bootstrap 的 typeahead 插件。如果需要解析它,您可以使用以下类似的内容:

jQuery.parseJSON( placeUrls );

注意,你也可以省略 locals: {}。


关于不使用本地变量的注意事项,我对此并不了解,谢谢你的提醒。 - braitsch
1
“略过本地变量”是什么意思?@braitsch - mathakoot
@iamrudra 在渲染函数中,不必在locals对象前加上“locals”一词。例如“res.render('home', { { data : myObject } })”就可以了,而不是“res.render('home', { locals : { data : myObject } })”。尽管出于清晰起见,我建议你仍然这样做。 - braitsch
@braitsch:这对我来说是新的东西。我在学校做过Node和Express项目,但从未使用过locals对象。您能指向正确的文档/阅读材料吗? - mathakoot

3
使用Jade模板技术:
如果你想在一个静态的HTML文件中插入@Amberlamps的代码片段,记得在头部添加!!! 5,避免你的样式被破坏。在views/index.jade中进行操作。
!!! 5
script(type='text/javascript')
    var local_data =!{JSON.stringify(data)}

include ../www/index.html

这将在实际静态HTML页面加载之前传递您的local_data变量,以便从开始全局使用该变量。
服务器端(使用Jade模板引擎)- server.js:
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');

app.get('/', ensureAuthenticated, function(request, response){
    response.render('index', { data: {currentUser: request.user.id} });
});

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

1
您不需要在渲染调用中传递本地变量,本地变量是全局的。在您的pug文件中,请勿使用键表达式,例如#{}。只需使用类似以下内容的内容: base(href=base.url) 其中base.urlapp.locals.base = { url:'/' };

-1

你听说过socket.io吗?(http://socket.io/)。 从express中访问对象的一种简单方法是在node.js和你的javascript之间建立一个socket。这样数据就可以轻松地传递到客户端,然后使用javascript进行简便操作。代码不需要很多,只需在node.js中使用socket.emit(),在客户端使用socket.on()。我认为这会是一个有效的解决方案!


19
这就像用锤子去打蜜蜂一样,这会增加大量请求,但却几乎没有什么作用。 - Arnaud Rinquin
确切地说,在这种情况下,Socket.io 是过度设计了。我正在寻找最轻量级的方式将 JS 变量从服务器传输到客户端脚本。 - braitsch
3
LOL,我有点喜欢Alex的建议,啪!啊,所有那些讨厌的当地人啪啪都消失了! - user1323136

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接