【问题标题】:How can I render a Handlebars file using two different routes?如何使用两条不同的路线渲染 Handlebars 文件?
【发布时间】:2018-04-03 05:17:00
【问题描述】:

我正在从新闻网站上抓取文章。我已经成功抓取了文章,数据也顺利到达了前端。 (它的 console.log 是正确的)。我的问题是我无法使用按钮将数据呈现到页面上 - 数据仅在我刷新时才会填充页面。

我知道这个问题与 Handlebars 有关,因为如果我尝试使用 jQuery 渲染页面,它可以工作。

我相信这与我的路线有关。我通过 /articles 路由将数据发送到页面,但正如您所见,我并没有专门使用 res.render 或 res.redirect。我认为这就是为什么它不起作用?但是,我不知道如何解决它。我对路线和回调有点动摇。我是编码新手,但我向您保证,我已经研究并尝试了许多修复方法,但均无济于事。任何帮助或指导表示赞赏。谢谢你。

app.get("/", function(req, res) {
  db.Article
  .find({})
  .then(function(dbArticle) {
  // res.render("index");
  res.render("index", { articles : dbArticle });
});
});

app.get("/scrape", function(req, res) {
  axios.get("https://www.nytimes.com/section/technology?
  action=click&pgtype=Homepage&region=TopBar&module=HPMini
Nav&contentCollection=Tech&WT.nav=page")
  .then(function(response) {

  var $ = cheerio.load(response.data);

  $("a.story-link").each(function(i, element) {
    var results = {};
    results.link = $(this).attr("href");
    // console.log("This is my link " + results.link)
    results.blurb = $(this).children().find(".summary").text();
    // console.log("This is my blurb " + results.blurb)
    results.headline = $(this).children().find(".headline").text();
    // console.log("This is my headline " + results.headline)

  db.Article
    .create(results)
    .then(function(dbArticle) {
      res.json(dbArticle);  
      // res.end();

      // console.log("YES", dbArticle);
    })
    .catch(function(err) {
    res.json(err);
  })
  })
})
})

app.get("/articles", function(req, res) {
  db.Article
    .find({})
    .then(function(dbArticle) {
      res.json(dbArticle);
      // console.log(dbArticle, "scraped")
    })
    .catch(function(err) {
      res.json(err);
    });
});`

【问题讨论】:

  • 当您说“/articles route”时,您的意思是/scrape?您是使用 AJAX 还是表单提交来调用此路由或...什么?
  • 我使用 /scrape 来抓取文章和 /articles 将数据库信息带到页面。我使用 $.getJSON 使用按钮(单击事件)调用此路由。
  • 对不起,我的错误,我错过了你的代码示例的结尾。我不清楚你想做什么。如果您使用的是 AJAX 请求,那么通常您会返回 JSON 数据并使用 JavaScript 更新客户端中的页面。在服务器上使用res.render(通常)用于生成 HTML,加载新页面。可以有一个中间立场,AJAX 请求返回一小段 HTML 以注入页面,但所有内容都需要专门编写以支持它。您尝试使用哪种方法?您能否详细说明“如果我尝试使用 jQuery 渲染页面,它可以工作”?
  • 我有一个 app.js 文件,它有这个点击事件: $('#show-articles').on("click", function (event) { event.preventDefault(); $.getJSON("/articles", function(data) { for (var i = 0; i
  • 很抱歉,格式不正确。当我使用这个点击事件代码时,我可以在页面上看到“data[i].headline”文本(来自我的数据库和抓取的文章)。但是,我正在尝试使用 Handlebars。我似乎不知道如何使用 Handlebars 更新页面上的 html。

标签: javascript node.js express handlebars.js


【解决方案1】:

您在某个地方出现了概念上的脱节。我不确定具体在哪里,所以我将尝试涵盖一些基础知识,希望我可以在此过程中的某个地方填补相关空白。

假设您的 Express 服务器在 localhost:3000 运行。它在localhost 上并不重要,如果代码在世界另一端的计算机上运行,​​一切都会大同小异。关键是浏览器看不到你的代码,它对 HTML、CSS、JavaScript 等有一些天生的了解,但对你的应用程序一无所知。

当您在浏览器地址栏中键入 URL http://localhost:3000/ 时,它会在运行于 localhost:3000 的服务器上发出 HTTP GET 请求,请求路径 /。请求只是“通过线路”发送的一系列字节(当我说“线路”时,请考虑网络电缆,即使实际上不涉及物理网络电缆)。使用浏览器的开发工具查看网络部分并跟踪此特定请求。单击它并查看请求详细信息。

浏览器完全不知道服务器中发生了什么。请记住,就浏览器而言,此服务器是“半个地球”。

当此请求到达您的服务器时,它将转到您在app.get('/', ... 注册的路由。对res.render 的调用将运行Handlebars 模板并生成一串HTML 标记。然后将该字符串转换为字节,并将这些字节作为响应主体“通过网络”发送回。响应还有一些标头来描述正文中的内容,但最终都是按顺序发送的大量字节。

这个响应是返回给浏览器的。它不知道它是如何生成的。就浏览器而言,它可以很容易地从静态文件中提供。它不在乎。它采用这个 HTML 标记(即一大块文本)并对其进行解析以创建一个 DOM 树。这是一个适当的数据结构,重要的是要认识到它与相应的标记不同(尽管密切相关)。关于术语的简要说明,我们在标记中称为 tag 的东西在 DOM 中变成了 element 节点。然后浏览器获取这棵 DOM 节点树并使用它们生成页面的图像,这就是在浏览器视口中实际显示给用户的内容。

为什么我一直在谈论“通过网络发送字节”?不一切都只是字节吗?嗯,是的,不是的。如果你有一个像数组或对象这样的数据结构,你不能只是“通过网络”发送它。它可能使用计算机内存中的字节表示,但要将其作为 HTTP 请求/响应的一部分发送,需要将其转换为将其表示为线性字节序列的格式,一个接一个。此过程通常称为序列化。一串 HTML 标记是 HTML DOM 节点的序列化表示,就像 JSON 通常用作序列化 JavaScript 数据结构(如对象和数组)的一种方式。

所以,回到我们的/ 请求,如果您查看浏览器开发工具的网络部分,您将能够看到请求和响应。它以格式良好的方式呈现,因此您不必自己尝试读取原始字节序列。重申关键点,浏览器只知道 HTTP 响应告诉它什么,它不知道您使用 Handlebars 来创建 HTML 标记。

当然,标记可能包含其他资源的 URL,例如图像、CSS、JavaScript 等。当浏览器将标记解析为 DOM 节点时,它会遇到这些其他 URL,并在需要时为每个 URL 发出单独的 HTTP 请求。这些请求中的每一个都与原始 / 请求的行为方式大致相同,并且浏览器再次不知道服务器如何生成响应:它可能正在返回静态文件的内容,或者它可能正在生成整个即时响应。

所以现在我们来到更新页面以响应用户交互的主题。

也许最容易理解的交互是单击链接,即像<a href="/other-url">click</a> 这样的锚标记。这只是更新浏览器地址栏中的 URL 并丢弃上一页。

HTML 表单与此类似,但它可以执行各种其他技巧,例如POST 请求。表单是使用<form> 元素构建的(也许更准确地说,我应该说form 元素,没有<>,但我认为<form> 更容易理解,只要我们清楚区别标记和 DOM 元素中的标签之间)。出于本描述的目的,它们与锚链接非常相​​似。浏览器 URL 得到更新,并在其位置加载新页面。 (我不打算在这里介绍它,但是有一些方法可以提交表单,这样当前页面就不会改变)。

这两种方法都会丢弃页面并从服务器重新加载它。多年来,这就是动态网站的构建方式。然后 AJAX 出现了。

使用 AJAX,您可以在 JavaScript 中执行 HTTP 请求,而无需更改当前页面位置。浏览器并没有真正尝试理解从 AJAX 请求返回的响应,它只是进行一些基本解析,然后将其交给您注册的任何回调。

这让我们回到你原来的例子。您有一个 AJAX 请求调用服务器并拉回一些 JSON 数据。然后,您使用 jQuery 使用该 JSON 来相应地更新页面的 DOM 节点。这是一个非常好的方法,但让我们谈谈一些替代方案。

目标是更新页面以反映新数据,就像我们刷新整个页面一样。为此,我们需要使用 jQuery 构建与 Handlebars 生成的标记中完全相同的 DOM 节点结构。这明显违反了 DRY 原则,因为我们在两个地方重复了相同的结构。

删除这种重复的一种方法是始终使用 AJAX 加载页面的该部分。使用 Handlebars(在服务器上)初始呈现页面会使页面的那部分空白,然后在页面加载时使用一些客户端 JavaScript 启动 AJAX 请求。这可能会稍微延迟页面的初始加载时间,也可能会导致 SEO 出现问题,但对于许多 SPA 而言,这些都不是重要问题,因此这是使用的方法。

在光谱的另一端,我们可以重新加载页面。我们可以通过执行 AJAX 请求然后在该请求成功后单独重新加载页面来做到这一点(例如使用window.location.reload())。我们也可以完全取消 AJAX 请求。相反,我们可以向相关的更新 URL 提交一个表单。服务器将对数据执行相关更改,然后返回新呈现的页面,很可能使用对 res.render 的调用,就像它对 / 路由所做的那样。

然后我们有一些介于两者之间的解决方案。

我们仍然可以使用 AJAX 请求并更改响应,以便返回 HTML 标记以注入页面的相关部分,而不是返回 JSON。我们需要在服务器上使用合适的部分拆分 Handlebars 模板,这样我们就可以只生成我们需要的一小部分 HTML,而不是整个页面的 HTML。

另一个中间解决方案是在两端安装把手。数据仍将使用 JSON 加载,但将通过浏览器中的 Handlebars 模板运行。要使其正常工作,您需要将相关的 Handlebars JS 文件加载到浏览器中。您还需要找到一种将相关模板导入浏览器的方法,或者使用 AJAX 请求加载它,或者更有可能在服务器上初始呈现页面时注入它。

所以这是四种不同的方法,我们还远远没有穷尽可能性。鉴于这是一个学习练习,而不是现实世界的问题,实际上不可能确定哪种方法最好,每种方法都有利有弊。我认为最重要的一点是让您清楚哪些代码在服务器上运行,哪些代码在客户端运行,以及两者如何通过 HTTP 请求进行交互。

【讨论】:

  • 非常感谢。您的回复非常有帮助 - 我很感激您的解释,因为我一直遇到困难!我能够解决这个问题,并将很快解释!同时,我想快速回复并感谢您的宝贵时间。
  • 我的问题在于我的路由。我试图同时抓取文章并将它们保存到数据库中,而我只想通过点击事件来抓取文章。一旦我将这两个实例分成两条路线(一个用于查找抓取文章的帖子和一个用于获取我保存在数据库中的内容的帖子),我就能够正确地将信息呈现到页面上。再次感谢。
猜你喜欢
  • 2023-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-20
  • 1970-01-01
  • 2019-02-14
  • 2018-08-25
相关资源
最近更新 更多