如何将变量从玉模板文件传递到脚本文件?


119

我在未传递到javascript文件的玉模板文件(index.jade)中声明的变量(config)遇到问题,这会使我的javascript崩溃。这是文件(views / index.jade):

h1 #{title}

script(src='./socket.io/socket.io.js')
script(type='text/javascript')
  var config = {};
  config.address = '#{address}';
  config.port = '#{port}';
script(src='./javascripts/app.js')

这是我的app.js(服务器端)的一部分:

  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.set('address', 'localhost');
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});

// Routes

app.get('/', function(req, res){
  res.render('index', {
    address: app.settings.address,
    port: app.settings.port
});
});

if (!module.parent) {
  app.listen(app.settings.port);
  console.log("Server listening on port %d",
app.settings.port);
}

// Start my Socket.io app and pass in the socket
require('./socketapp').start(io.listen(app));

这是我的javascript文件崩溃的一部分(public / javascripts / app.js):

(function() {
        var socket = new io.Socket(config.address, {port: config.port, rememberTransport: false});

我在本地主机(我自己的机器)上的开发模式(NODE_ENV = development)上运行站点。我正在使用node-inspector进行调试,它告诉我config变量在public / javascripts / app.js中未定义。

有任何想法吗??谢谢!!


Answers:


174

这是一个有点晚了,但...

script.
  loginName="#{login}";

在我的脚本中,这工作正常。在Express中,我正在这样做:

exports.index = function(req, res){
  res.render( 'index',  { layout:false, login: req.session.login } );
};

我猜最新的玉是不同的吗?

Merc。

编辑:添加了“。” 脚本后,以防止翡翠警告。


1
感谢您接受答案!那么,您的代码的实际问题是什么?
Merc

是的,我也通过将局部变量包装在模板中的引号和#{}指示符中来工作。
Askdesigners

此答案很危险,lagginreflex的答案正确编码了字符串(登录名中的换行符可能允许代码执行)。
保罗·格罗夫

所以问题只是单引号,而双引号是对的,对吗?
奥古斯丁·里丁格

对我来说,问题是点。我在脚本块的末尾有它。在“ script”关键字后面加上句点,它就像一个超级按钮。谢谢!
Mirko

102

!{}用于未转义的代码插值,更适合于对象

script var data = !{JSON.stringify(data).replace(/<\//g, '<\\/')}

{ foo: 'bar' }
// becomes:
<script>var data = {"foo":"bar"}</script>

{ foo: 'bar</script><script>alert("xss")//' }
// becomes:
<script>var data = {"foo":"bar<\/script><script>alert(\"xss\")//"}</script>

这个想法是为了防止攻击者:

  1. 突破变量:JSON.stringify对引号进行转义
  2. 突破脚本标记:如果变量内容(如果来自数据库,则可能无法控制)具有</script>字符串,则replace语句将处理该字符串

https://github.com/pugjs/pug/blob/355d3dae/examples/dynamicscript.pug


#{}是用于转义字符串插值的,仅在使用字符串时才适用。它不适用于对象

script var data = #{JSON.stringify(data)}

//=> <script>var data = {&quot;foo&quot;:&quot;bar&quot;}</script>

1
很好的答案!我已经在这个主题上搜索了20多分钟,没有其他答案说明!{}和#{}之间的区别。我一直以为!{]就是#{}的不赞成使用的版本……再次感谢。
Patrick E.

到顶部!好答案。
Scarysize '16

很好的答案,它适用于对象!顺便说一句,是JSON全局对象吗?它似乎无需导入任何其他库即可工作。如果是这样,那么浏览器支持又如何?
chharvey '16

@chharvey JSON是内置的javascript语言(例如Date或Math),所有浏览器都支持。
laggingreflex '16

1
同意这是最好的答案,但我认为这不是逃脱潜在危险字符串的正确位置。IMO最好省略replace此代码中的调用,并将此值与其他任何输入一样对待。保证JSON.stringify会生成有效的javascript(或失败),并且在内存中具有此潜在危险值之后,可以确保在使用前根据需要对其进行清理。
莱利云雀

9

就我而言,我试图通过快速路由(类似于OP设置)将对象传递到模板中。然后,我想将该对象传递给我通过pug模板中的脚本标签调用的函数。尽管lagginreflex的答案使我很接近,但最终得到了以下几点:

script.
    var data = JSON.parse('!{JSON.stringify(routeObj)}');
    funcName(data)

这样可以确保按预期方式传入对象,而无需在函数中反序列化。同样,其他答案似乎也适用于基元,但是当数组等与对象一起传递时,它们被解析为字符串值。


7

如果您像我一样,并且经常使用这种传递变量的方法,那么这是一种无需编写代码的解决方案。

在您的node.js路由中,将变量传递到名为的对象中window,如下所示:

router.get('/', function (req, res, next) {
    res.render('index', {
        window: {
            instance: instance
        }
    });
});

然后在您的pug / jade布局文件中(就在之前block content),将它们按如下方式取出:

if window
    each object, key in window
        script.
            window.!{key} = !{JSON.stringify(object)};

当我的layout.pug文件加载到每个pug文件时,我不需要一遍又一遍地“导入”变量。

这样,所有传递给window“魔术”的变量/对象最终都在window浏览器的真实对象中,您可以在Reactjs,Angular,...或普通javascript中使用它们。


1
这是我所见过的最好的答案。
托希尔

3

这是我解决此问题的方法(使用MEAN导数)

我的变量:

{
  NODE_ENV : development,
  ...
  ui_varables {
     var1: one,
     var2: two
  }
}

首先,我必须确保传递了必要的配置变量。MEAN使用节点nconf包,并且默认情况下设置为限制从环境传递哪些变量。我必须补救:

config / config.js:

原版的:

nconf.argv()
  .env(['PORT', 'NODE_ENV', 'FORCE_DB_SYNC'] ) // Load only these environment variables
  .defaults({
  store: {
    NODE_ENV: 'development'
  }
});

修改后:

nconf.argv()
  .env('__') // Load ALL environment variables
  // double-underscore replaces : as a way to denote hierarchy
  .defaults({
  store: {
    NODE_ENV: 'development'
  }
});

现在,我可以像这样设置变量:

export ui_varables__var1=first-value
export ui_varables__var2=second-value

注意:我将“层次结构指示器”重置为“ __”(双下划线),因为其默认值为“:”,这使得从bash设置变量更加困难。在此主题上查看另一篇文章。

现在是玉器部分:接下来,需要渲染值,以便javascript可以在客户端将其提取。将这些值写入索引文件的一种直接方法。由于这是一个单页应用程序(角度),因此始终首先加载此页面。我认为理想情况下,这应该是一个javascript包含文件(只是为了保持环境整洁),但这对演示很有帮助。

app / controllers / index.js:

'use strict';
var config = require('../../config/config');

exports.render = function(req, res) {
  res.render('index', {
    user: req.user ? JSON.stringify(req.user) : "null",
    //new lines follow:
    config_defaults : {
       ui_defaults: JSON.stringify(config.configwriter_ui).replace(/<\//g, '<\\/')  //NOTE: the replace is xss prevention
    }
  });
};

app / views / index.jade:

extends layouts/default

block content
  section(ui-view)
    script(type="text/javascript").
    window.user = !{user};
    //new line here
    defaults = !{config_defaults.ui_defaults};

在我渲染的html中,这给了我一个漂亮的小脚本:

<script type="text/javascript">
 window.user = null;         
 defaults = {"var1":"first-value","var2:"second-value"};
</script>        

从这一点出发,angular可以轻松使用代码。


这是说出已经接受的答案已经很长时间的a绕方式。此外,提及MEAN,nconf等均无关紧要。问题是关于如何将变量传递给客户端,而不是关于如何进行开发堆栈。不过,请不要犹豫,因为您仍然在此答案中付出了很多努力。
Mrchief

过于复杂
andygoestohollywood

2

看到以下问题:JADE + EXPRESS:迭代内联JS代码中的对象(客户端)?

我有同样的问题。Jade不会在Java脚本中传递局部变量(或根本不做任何模板处理),而只是将整个块作为文字文本传递。如果您在Jade文件中的脚本标签上方使用局部变量“地址”和“端口”,它们应该显示出来。

我上面链接到的问题中列出了可能的解决方案,但是您可以:-以不转义的文本形式传递每行(!=每行开头),然后在使用局部变量,或:-通过dom元素传递变量,并通过JQuery访问(丑陋)

有没有更好的办法?似乎Jade的创建者不希望多行javascript支持,如GitHub中此线程所示:https : //github.com/visionmedia/jade/pull/405

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.