有没有办法从Node.JS应用程序重定向到HTML文件,如:Monitoring and Instrumentation表达式,并将JSON数据传递给html文件?
答案 0 :(得分:25)
我知道这已经很晚了,但我想提供一个其他人都没有提供的解决方案。此解决方案允许将文件流式传输到响应,同时仍允许您修改内容,而无需模板引擎或将整个文件缓冲到内存中。
让我先说明为什么res.sendFile
对于那些不了解的人来说是如此理想。由于Node是单线程的,它通过连续执行大量非常小的任务来工作 - 这包括从文件系统读取并回复http请求。在任何时候,Node都不会停止它正在做的事情并从文件系统中读取整个内容。它会读一点,做别的事情,多读一点,做别的事情。回复http请求和Node中的大多数其他操作也是如此(除非您明确使用sync
版本的操作 - 例如readFileSync - 如果您可以帮助它,请不要这样做,严肃地说,不要 - 这是自私的)。
考虑10个用户请求同一文件的情况。效率低下的事情是将整个文件加载到内存中,然后使用res.send()
发送文件。即使它是相同的文件,该文件也会在被发送到浏览器之前分别加载到存储器10中。然后垃圾收集器需要在每次请求后清理这个混乱。代码将被无辜地写成:
app.use('/index.html', (req, res) => {
fs.readFile('../public/index.html', (err, data) => {
res.send(data.toString());
});
});
这似乎是正确的,而且有效,但效率非常低。由于我们知道Node以小块的形式处理事情,因此最好的做法是在从文件系统读取数据时将小块数据发送到浏览器。这些块永远不会存储在内存中,您的服务器现在可以处理更多的流量。这个概念称为流式传输,它是res.sendFile
的作用 - 它将文件直接从文件系统流式传输给用户,并为更重要的事情保留内存。以下是您手动执行的操作方式:
app.use('/index.html', (req, res) => {
fs.createReadStream('../public/index.html')
.pipe(res);
});
如果您希望在对文件稍作修改的同时继续将文件流式传输给用户,那么此解决方案适合您。请注意,这不是模板引擎的替代品,而是应该用于在文件流式传输时对文件进行小的更改。下面的代码会将带有数据的小脚本标记附加到HTML页面的主体。它还显示了如何在http响应流中添加或附加内容:
const Transform = require('stream').Transform;
const parser = new Transform();
parser._transform = function(data, encoding, done) {
const str = data.toString().replace('</body>', '<script>var data = {"foo": "bar"};</script></body>');
this.push(str);
done();
};
// app creation code removed for brevity
app.use('/index.html', (req, res) => {
res.write('<!-- Begin stream -->\n');
fs
.createReadStream('../public/index.html')
.pipe(parser)
.on('end', () => {
res.write('\n<!-- End stream -->')
}).pipe(res);
});
答案 1 :(得分:13)
您从给定的请求中得到一个回复。您可以将多个内容组合到一个响应中,也可以要求客户端单独发出请求以获取单独的内容。
如果您要做的是获取HTML文件并通过在其中插入一些JSON来修改它,那么您不能仅使用res.sendFile()
因为它只是从磁盘或缓存中读取文件而直接将其作为响应流式传输,不提供修改它的机会。
更常见的方法是使用模板系统,允许您将内容插入HTML文件(通常用您自己的数据替换特殊标记)。实际上有数百个模板系统和许多支持node.js的模板系统。 node.js的常见选择是Jade(Pug),Handlebars,Ember,Dust,EJS,Mustache。
或者,如果您真的想这样做,您可以将HTML文件读入内存,对其使用某种.replace()
操作来插入您自己的数据,然后res.send()
生成的更改文件
答案 2 :(得分:3)
嗯,它有点老,但我没有看到任何足够的答案,除了&#34;为什么不&#34;。你有办法在静态文件中传递参数。这很容易。考虑在您的来源上使用以下代码(使用快递):
let data = fs.readFileSync('yourPage.html');
if(data)
res.send(data.replace('param1Place','uniqueData'));
//else - 404
现在举例来说,只需在 yourPage.html 中设置一个Cookie,例如:
<script>
var date = new Date();
document.cookie = "yourCookieName='param1Place';" +
date.setTime(date.getTime() + 3600) + ";path=/";
</script>
您可以在js中明确地从yourCookieName中提取 uniqueData 的内容
答案 3 :(得分:1)
您只能从服务器返回一个响应。最常见的事情是使用nunjucks或jade在服务器上模拟文件。另一种选择是在客户端上呈现文件,然后使用javascript对服务器进行ajax调用以获取其他数据。我想你也可以在cookie中设置一些数据,然后通过javascript在客户端读取它。
答案 4 :(得分:1)
为什么不只读取文件,应用转换然后在回调中设置路由?
fs.readFile(appPath, (err, html) => {
let htmlPlusData = html.toString().replace("DATA", JSON.stringify(data));
app.get('/', (req, res) => {
res.send(htmlPlusData);
});
});
请注意,您无法动态更改data
,必须重新启动节点实例。
答案 5 :(得分:0)
(除非您想模板化html文件以将json数据插入到脚本标记中)。您需要公开api端点以表示将数据发送到页面,并在页面上具有访问它的功能。例如,
// send the html
app.get('/', (req, res) => res.sendFile('index'));
// send json data
app.get('/data', (req, res) => res.json(data));
现在,在客户端,您可以创建访问此端点的请求
function get() {
return new Promise((resolve, reject) => {
var req = new XMLHttpRequest();
req.open('GET', '/data');
req.onload = () => resolve(req.response);
});
}
// then to get the data, call the function
get().then((data) => {
var parsed = JSON.parse(data);
// do something with the data
});
编辑:
因此箭头函数可能不适用于客户端。确保用真实代码中的function(){}替换它们
答案 6 :(得分:0)
如果您确实想修改HTML文件中的内容,我认为Ryan Wheale发布的答案是最好的解决方案。您也可以使用cheerio处理复杂的逻辑。
但是对于这个我们只想将一些数据从服务器传递给客户端的特定问题,实际上根本不需要将index.html
读入内存。
您只需在HTML文件顶部的某个位置添加以下脚本标记:
<script src="data.js"></script>
然后让Express将所需的任何数据提供给该文件:
app.get("/data.js", function (req, res) {
res.send('window.SERVER_DATA={"some":"thing"}');
});
然后可以使用以下窗口对象轻松地在客户端应用程序中的任何位置引用此数据:window.SERVER_DATA.some
React前端的其他上下文:
如果您的客户端和服务器在不同的端口上运行,例如在create-react-app上,则此方法特别有用,因为代理服务器始终可以响应对data.js
的请求,但是当您正在使用Express向index.html
中插入内容,那么在插入任何数据之前,始终需要准备好index.html
的生产版本。