Express - 在单个请求中将页面和自定义数据发送到浏览器?

时间:2014-01-16 21:07:14

标签: javascript html node.js http express

如何同时呈现页面并将自定义数据传输到浏览器。据我所知,它需要发送两层:第一层是模板,第二层是JSON数据。我想通过骨干来处理这些数据。

正如我从教程expressbb app所理解的那样,互动如下:

  1. res.render向浏览器发送页面
  2. document.ready触发jQuery.get到app.get('/post')
  3. app.get('/post', post.allPosts)将数据发送到页面
  4. 这是三个步骤,如何一个人完成?

    var visitCard = {
      name: 'John Smit',
      phone: '+78503569987'
    };
    
    exports.index = function(req, res, next){
      res.render('index');
      res.send({data: visitCard}); 
    };
    

    我应该如何在页面上捕捉到这个变量 - document.card

5 个答案:

答案 0 :(得分:9)

我创建了自己的小中间件函数,它将一个名为renderWithData的辅助方法添加到res对象。

app.use(function (req, res, next) {
    res.renderWithData = function (view, model, data) {
        res.render(view, model, function (err, viewString) {
            data.view = viewString;
            res.json(data);
        }); 
    };
    next();
});

它包含视图名称,视图模型以及要发送到浏览器的自定义数据。它调用res.render但传入回调函数。这指示express将编译的视图标记作为字符串传递给回调,而不是立即将其传递给响应。获得视图字符串后,我将其作为data.view添加到数据对象中。然后我使用res.json将数据对象发送到带有编译视图的浏览器:)

编辑:

上面提到的一个警告是,请求需要使用javascript进行,因此它不能是完整页面请求。您需要一个初始请求来下拉包含将发出ajax请求的javascript的主页面。

这非常适合在用户通过AJAX导航到新页面时尝试更改浏览器URL和标题的情况。您可以将新页面的部分视图与页面标题的一些数据一起发送回浏览器。然后,您的客户端脚本可以将部分视图放在页面上,更新页面标题栏,并在需要时更新URL。

如果您想要将完整的HTML文档与一些初始JavaScript数据一起发送到浏览器,那么您需要将该JavaScript代码编译到视图本身。绝对有可能做到这一点,但我从来没有找到一种不涉及一些字符串魔法的方式。

例如:

// controller.js
var someData = { message: 'hi' };
res.render('someView', { data: JSON.stringify(someData) });

// someView.jade
script.
    var someData = !{data};

注意:使用!{data}代替#{data},因为默认情况下,jade会转义HTML,这会将所有引号转换为"占位符。

起初看起来真的很奇怪,但它确实有效。基本上,您在服务器上获取JS对象,将其转换为字符串,将该字符串呈现在已编译的视图中,然后将其发送到浏览器。当文档最终到达浏览器时,它应如下所示:

// someSite.com/someView
<script type="text/javascript">
    var someData = { "message": "hi" };
</script>

希望这是有道理的。如果我要重新创建我的原始辅助方法来缓解第二种情况的痛苦,那么它看起来像这样:

app.use(function (req, res, next) {
    res.renderWithData = function (view, model, data) {
        model.data = JSON.stringify(data);
        res.render(view, model);
    };
    next();
});

所有这一切都是获取您的自定义数据对象,为您进行字符串化,将其添加到视图的模型,然后正常呈现视图。现在你可以致电res.renderWithData('someView', {}, { message: 'hi' });;您只需确保视图中的某个位置抓取该数据字符串并将其呈现为变量赋值语句。

html
    head
        title Some Page
        script.
            var data = !{data};

不会撒谎,这整件事情有点粗暴,但是如果它能为你节省额外的服务器,那就是你所追求的那样,那就是你需要做的事情。也许有人可以想到一些更聪明的东西,但我只是不知道你将如何获得数据已经存在于第一次呈现的完整HTML文档中。

EDIT2:

以下是一个工作示例:https://c9.io/chevex/test

您需要拥有(免费)Cloud9帐户才能运行该项目。登录,打开app.js,然后单击顶部的绿色运行按钮。

答案 1 :(得分:4)

我的方法是发送包含该信息的cookie,然后从客户端使用它。

<强> server.js

const visitCard = {
  name: 'John Smit',
  phone: '+78503569987'
};

router.get('/route', (req, res) => {
  res.cookie('data', JSON.stringify(pollsObj));
  res.render('index');
});

<强> client.js

const getCookie = (name) => {
  const value = "; " + document.cookie;
  const parts = value.split("; " + name + "=");
  if (parts.length === 2) return parts.pop().split(";").shift();
};

const deleteCookie = (name) => {
  document.cookie = name + '=; max-age=0;';
};

const parseObjectFromCookie = (cookie) => {
  const decodedCookie = decodeURIComponent(cookie);
  return JSON.parse(decodedCookie);
};

window.onload = () => {
  let dataCookie = getCookie('data');
  deleteCookie('data');

  if (dataCookie) {
    const data = parseObjectFromCookie(dataCookie);
    // work with data. `data` is equal to `visitCard` from the server

  } else {
    // handle data not found
  }


操作实例

从服务器,您在呈现页面之前发送cookie,因此在页面加载时cookie可用。

然后,从客户端,您获得我找到here解决方案的cookie并将其删除。 cookie的内容存储在我们的常量中。如果cookie存在,则将其解析为对象并使用它。请注意,在parseObjectFromCookie内部,首先必须解码内容,然后将JSON解析为对象。

备注

  • 如果您异步获取数据,请务必在呈现前发送 Cookie。否则,您将收到错误,因为res.render()结束了响应。如果数据获取时间过长,您可以使用另一个不长时间保存渲染的解决方案。另一种方法是从客户端打开套接字并发送您在服务器中保存的信息。该方法See here

  • 可能data不是Cookie的最佳名称,因为您可能会覆盖某些内容。使用对你的目的更有意义的东西。

  • 我在其他任何地方都找不到这个解决方案。我不知道是否由于某种原因我不建议使用cookies。我不知道。我只是认为它可以工作并检查它,但我还没有在生产中使用它。

答案 2 :(得分:3)

使用res.send代替res.render。它接受任何形式的原始数据:字符串,数组,普通旧对象等。如果它是对象或对象数组,它会将它序列化为JSON。

var visitCard = {
  name: 'John Smit',
  phone: '+78503569987'
};

exports.index = function(req, res, next){
  res.send(visitCard}; 
};

答案 3 :(得分:2)

查看Steamer,这是为此目的而制作的一个小模块。

https://github.com/rotundasoftware/steamer

答案 4 :(得分:0)

最优雅,最简单的方法是使用渲染引擎(至少对于所关注的页面)。例如使用ejs引擎

node install ejs -s

在server.js上:

let ejs = require('ejs');    
app.set('view engine', 'ejs');

然后将所需的index.html页面重命名为index.ejs并将其移至/ views目录。之后,您可以通过使用mysql模块为该页面设置API endpoit:

app.get('/index/:id', function(req, res) { 
    db.query("SELECT * FROM products WHERE id = ?", [req.params.id], (error, results) => {
    if (error) throw error;
        res.render('index', { title: results[0] }); 
    });
});  

在前端,您将需要发出GET请求,例如使用Axios或直接单击发送请求的模板index.ejs页面中的链接:

<a v-bind:href="'/index/' + co.id">Click</a>

其中co.id是您要与请求一起发送的Vue数据参数值'co'