Nodejs后端开发问题求解惑

Nodejs后端开发问题求解惑

纯新手,我在看了几个node项目之后,产生一个想法,不知道正确否,想请各位为我解惑. 用cnode社区项目来说吧,cnode渲染一张页面的时候,过程大概是这样的 1-接受请求 2-路由选择,跳转到对应的中间件处理,中间件的过程大致如此

func 1--得到数据a
func 2--在1的回调函数中得到数据b
func 3--在2的回调函数中得到数据c
...

在最后的一个回调函数中把这些数据传递到渲染模块,然后返回给浏览器.

这和我以前开发javaweb的套路很类似.

我感觉这还是一种顺序执行.如果假设所有的数据没有依赖要求,能不能把每个数据都当成单独的连接请求发给浏览器,当浏览器请求页面的时候,直接渲染一张框架,然后在浏览器端来请求这些数据,这样所有的数据都是异步给出去.而不是在服务端整理好一起渲染出来给用户.既然node的优势是高并发,那么这种方案是否能够得到更好一点呢?


6 回复

当然可以!你的想法非常有趣,而且确实符合Node.js的一些核心优势。我们可以通过一些具体的例子来探讨一下这个问题。

背景

首先,让我们回顾一下传统的后端渲染流程:

  1. 接收请求:客户端发送请求到服务器。
  2. 路由选择:根据请求路径,选择相应的路由处理器。
  3. 数据获取:通过多个中间件或异步操作获取数据。
  4. 渲染页面:将所有数据整合后,生成最终的HTML页面并返回给客户端。

改进思路

你提到的想法是将各个数据获取过程视为独立的异步请求,从而减轻服务器端的压力,并利用浏览器端的能力来处理数据。这种方式不仅可以提高并发性能,还可以提升用户体验。

示例代码

下面是一个简单的示例,展示了如何实现这种改进思路。我们将使用Express框架作为后端,并使用Axios库来发起HTTP请求。

安装依赖

npm install express axios

后端代码 (server.js)

const express = require('express');
const axios = require('axios');
const app = express();

app.get('/data', async (req, res) => {
    try {
        const [dataA, dataB, dataC] = await Promise.all([
            axios.get('http://localhost:3000/api/dataA'),
            axios.get('http://localhost:3000/api/dataB'),
            axios.get('http://localhost:3000/api/dataC')
        ]);

        // 返回基本的HTML框架
        res.send(`
            <html>
                <head>
                    <title>Data Page</title>
                </head>
                <body>
                    <div id="content">
                        <!-- Data will be injected here -->
                    </div>
                    <script src="/client.js"></script>
                </body>
            </html>
        `);
    } catch (error) {
        console.error(error);
        res.status(500).send('Internal Server Error');
    }
});

// 模拟API端点
app.get('/api/dataA', (req, res) => {
    setTimeout(() => {
        res.json({ data: 'Data A' });
    }, 1000);
});

app.get('/api/dataB', (req, res) => {
    setTimeout(() => {
        res.json({ data: 'Data B' });
    }, 1500);
});

app.get('/api/dataC', (req, res) => {
    setTimeout(() => {
        res.json({ data: 'Data C' });
    }, 2000);
});

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});

前端代码 (client.js)

document.addEventListener('DOMContentLoaded', () => {
    const fetchAndRenderData = async () => {
        try {
            const [dataA, dataB, dataC] = await Promise.all([
                axios.get('http://localhost:3000/api/dataA'),
                axios.get('http://localhost:3000/api/dataB'),
                axios.get('http://localhost:3000/api/dataC')
            ]);

            const contentDiv = document.getElementById('content');
            contentDiv.innerHTML = `
                <h1>Data A</h1>
                <p>${dataA.data}</p>
                <h1>Data B</h1>
                <p>${dataB.data}</p>
                <h1>Data C</h1>
                <p>${dataC.data}</p>
            `;
        } catch (error) {
            console.error(error);
        }
    };

    fetchAndRenderData();
});

解释

  1. 后端代码

    • /data 路由返回一个基本的HTML框架。
    • 使用 Promise.all 并行获取数据,而不是按顺序获取。
  2. 前端代码

    • 使用 axios 在客户端获取数据并动态渲染页面。

这种方法不仅提高了并发性能,还减少了服务器端的数据处理负担,同时利用了现代浏览器的强大能力。希望这能帮助你更好地理解Node.js的异步处理方式!


这样客户端的开销比较大,多出一些http连接。带宽会浪费些。

前段优化里就有包括尽可能的合并资源文件以减少http的请求次数来自高页面渲染的效率和稳定性。http请求实在是太耗时且不可控。

你可以考虑bigpipe,以块的形式输出。可以做到在不增加http请求的情况下,达到极速输出。这是node的优势。

前端用angular渲染页面 后端提供纯RESTful的api ?

你的想法很有创意,将数据请求分散到客户端进行处理确实可以利用现代浏览器的高性能来减少服务器的负担。不过,在考虑这种方法之前,我们需要权衡一下其优缺点。

方法一:传统方式(服务器端组装数据)

这种方法与你提到的 CNode 社区项目中的处理方式类似。在服务器端获取所有数据并组装后,一次性发送给客户端。这种方式的优点在于用户体验较好,因为数据是在一次请求中完成的,用户等待时间较短。但缺点是增加了服务器端的压力,尤其是在数据获取操作较多的情况下。

示例代码:

const express = require('express');
const app = express();

app.get('/data', (req, res) => {
    func1((dataA) => {
        func2(dataA, (dataB) => {
            func3(dataB, (dataC) => {
                res.send({ a: dataA, b: dataB, c: dataC });
            });
        });
    });
});

function func1(callback) {
    // 获取数据a的逻辑
}

function func2(dataA, callback) {
    // 在dataA的基础上获取数据b的逻辑
}

function func3(dataB, callback) {
    // 在dataB的基础上获取数据c的逻辑
}

方法二:分散到客户端处理

你可以将数据获取分散到客户端,这样可以减轻服务器的负担。客户端可以分别请求这些数据,并且由于是异步的,所以不会阻塞其他请求。然而,这种方法会增加客户端的负担,需要编写更多的前端逻辑来处理这些数据。

示例代码:

服务器端:

const express = require('express');
const app = express();

app.get('/framework', (req, res) => {
    res.send('<html><body><div id="data-container"></div></body></html>');
});

app.get('/dataA', (req, res) => {
    // 获取数据a的逻辑
    res.json({ data: 'dataA' });
});

app.get('/dataB', (req, res) => {
    // 获取数据b的逻辑
    res.json({ data: 'dataB' });
});

app.get('/dataC', (req, res) => {
    // 获取数据c的逻辑
    res.json({ data: 'dataC' });
});

客户端(JavaScript):

document.addEventListener("DOMContentLoaded", function() {
    fetch('/dataA')
        .then(response => response.json())
        .then(dataA => {
            fetch('/dataB')
                .then(response => response.json())
                .then(dataB => {
                    fetch('/dataC')
                        .then(response => response.json())
                        .then(dataC => {
                            document.getElementById('data-container').innerHTML = `
                                <p>Data A: ${dataA.data}</p>
                                <p>Data B: ${dataB.data}</p>
                                <p>Data C: ${dataC.data}</p>
                            `;
                        });
                });
        });
});

总结

这两种方法各有优缺点。如果你的目标是减少服务器端的压力,提高并发能力,那么第二种方法可能更合适。但是,这也意味着你需要处理更多的前端逻辑。选择哪种方法取决于你的具体需求以及应用的整体架构。

回到顶部