MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Node.js服务器端渲染SSR技术介绍

2024-11-181.8k 阅读

Node.js 服务器端渲染 SSR 技术介绍

什么是服务器端渲染(SSR)

在传统的前端开发模式中,浏览器从服务器获取 HTML 文件,这个 HTML 文件通常只包含基本的结构和少量样式,页面的大部分内容(如数据填充、交互逻辑等)是通过客户端 JavaScript 在浏览器中执行后动态生成的。这种模式称为客户端渲染(CSR,Client - Side Rendering)。

而服务器端渲染(SSR,Server - Side Rendering)则是指在服务器端将网页的 HTML 结构和数据进行组合并渲染成完整的 HTML 页面,然后将这个完整的 HTML 页面发送给浏览器。浏览器只需要直接呈现这个已渲染好的页面,无需再通过客户端 JavaScript 进行大量的渲染工作。

SSR 的优势主要体现在以下几个方面:

  1. 首屏加载速度更快:对于 CSR 应用,浏览器需要先下载 HTML 文件,再下载 JavaScript 文件并执行,才能看到完整的页面内容。而 SSR 应用在服务器端就已经将页面渲染好,浏览器直接接收并展示,大大提高了首屏加载速度,提升用户体验。
  2. 利于搜索引擎优化(SEO):搜索引擎爬虫在抓取页面时,通常不会执行 JavaScript。CSR 应用在爬虫抓取时,可能只能获取到一个空的 HTML 骨架,无法获取到实际内容,不利于 SEO。而 SSR 应用发送给浏览器的是已经渲染好的包含完整内容的 HTML 页面,更容易被搜索引擎索引。
  3. 更好的可访问性:对于一些网络环境较差或者设备性能较低的用户,SSR 可以减少客户端的渲染压力,使得页面能够更快地呈现,提高了应用的可访问性。

Node.js 在 SSR 中的角色

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,它允许开发人员在服务器端使用 JavaScript 进行编程。Node.js 之所以在 SSR 中被广泛应用,主要有以下原因:

  1. 同构 JavaScript:前端开发人员可以使用熟悉的 JavaScript 语言进行服务器端开发,实现代码在前端和后端的部分复用,减少学习成本。例如,一些数据获取逻辑、路由逻辑等可以在前后端共享。
  2. 丰富的生态系统:Node.js 拥有庞大的 npm 包生态系统,开发人员可以轻松找到各种用于 SSR 的库和工具,如 Next.js、Nuxt.js 等,这些框架极大地简化了 SSR 应用的开发过程。
  3. 高性能:Node.js 采用事件驱动、非阻塞 I/O 模型,具有轻量级、高性能的特点,适合处理高并发请求,能够满足服务器端渲染对性能的要求。

SSR 的基本原理

  1. 服务器端渲染流程
    • 接收请求:服务器接收到客户端的 HTTP 请求。
    • 数据获取:服务器根据请求的路由等信息,从数据库、API 等数据源获取渲染页面所需的数据。
    • 模板渲染:服务器使用模板引擎(如 EJS、Pug 等)将获取的数据填充到 HTML 模板中,生成完整的 HTML 页面。
    • 返回响应:将渲染好的 HTML 页面返回给客户端浏览器。
  2. 客户端激活:当浏览器接收到服务器渲染的 HTML 页面后,会加载并执行客户端 JavaScript 代码。这一步主要是为了使页面具备交互性,例如为按钮添加点击事件等。客户端 JavaScript 代码会重新建立与服务器端数据的连接,实现状态的同步等操作。

使用 Express 和 EJS 实现简单的 SSR

  1. 环境搭建
    • 首先确保已经安装了 Node.js。
    • 创建一个新的项目目录,例如 ssr - demo,并在该目录下初始化 package.json 文件,运行命令 npm init -y
    • 安装 Express 和 EJS。Express 是一个流行的 Node.js web 应用框架,EJS 是一个简单的模板引擎。运行命令 npm install express ejs
  2. 项目结构
    • 在项目目录下创建以下文件和目录结构:
      • app.js:主程序文件,用于启动 Express 服务器并处理路由。
      • views 目录:存放 EJS 模板文件。在 views 目录下创建 index.ejs 文件。
  3. 编写 EJS 模板
    • views/index.ejs 文件中编写以下代码:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF - 8">
    <title>SSR Demo</title>
</head>
<body>
    <h1><%= message %></h1>
</body>
</html>

这里 <%= message %> 是 EJS 的语法,用于将数据插入到 HTML 模板中。 4. 编写 Express 服务器

  • app.js 文件中编写以下代码:
const express = require('express');
const app = express();
app.set('view engine', 'ejs');

app.get('/', (req, res) => {
    const data = {
        message: 'Hello from Server - Side Rendering!'
    };
    res.render('index', data);
});

const port = 3000;
app.listen(port, () => {
    console.log(`Server running on port ${port}`);
});

上述代码中,首先引入 Express 并创建一个 Express 应用实例。通过 app.set('view engine', 'ejs') 设置使用 EJS 作为模板引擎。在根路由 '/' 的处理函数中,定义了一个包含 message 数据的对象 data,然后使用 res.render('index', data)index.ejs 模板和数据进行渲染,并返回给客户端。 5. 运行项目

  • 在项目目录下运行命令 node app.js
  • 打开浏览器,访问 http://localhost:3000,可以看到页面上显示 Hello from Server - Side Rendering!

深入 SSR - 同构 React 应用

  1. React 与 SSR
    • React 是一个用于构建用户界面的 JavaScript 库。在传统的 React 应用开发中,通常采用客户端渲染的方式。要实现 React 应用的服务器端渲染,需要借助一些工具和技术。
    • React 提供了 react - dom/server 模块,其中的 renderToString 方法可以将 React 组件渲染为字符串形式的 HTML。
  2. 项目搭建
    • 创建一个新的项目目录,例如 react - ssr - demo,并初始化 package.json 文件(npm init -y)。
    • 安装所需的依赖:reactreact - domexpress。运行命令 npm install react react - dom express
  3. 编写 React 组件
    • 在项目目录下创建 src 目录,在 src 目录下创建 components 目录,然后在 components 目录下创建 App.js 文件,代码如下:
import React from'react';

const App = () => {
    return (
        <div>
            <h1>React Server - Side Rendering</h1>
        </div>
    );
};

export default App;
  1. 服务器端渲染代码
    • src 目录下创建 server.js 文件,代码如下:
const express = require('express');
const React = require('react');
const ReactDOMServer = require('react - dom/server');
const App = require('./components/App');

const app = express();

app.get('*', (req, res) => {
    const html = ReactDOMServer.renderToString(<App />);
    const page = `
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF - 8">
            <title>React SSR</title>
        </head>
        <body>
            <div id="root">${html}</div>
            <script src="/client - bundle.js"></script>
        </body>
        </html>
    `;
    res.send(page);
});

const port = 3000;
app.listen(port, () => {
    console.log(`Server running on port ${port}`);
});

上述代码中,引入 Express、React 和 react - dom/server。在 Express 的通配符路由 '*' 处理函数中,使用 ReactDOMServer.renderToStringApp 组件渲染为字符串形式的 HTML,并将其插入到 HTML 模板中,最后返回给客户端。 5. 客户端代码

  • src 目录下创建 client.js 文件,代码如下:
import React from'react';
import ReactDOM from'react - dom';
import App from './components/App';

ReactDOM.hydrate(<App />, document.getElementById('root'));

这里使用 ReactDOM.hydrate 方法在客户端激活 React 应用。hydrate 方法会将服务器端渲染的静态 HTML 与客户端 React 应用进行关联,使页面具备交互性。 6. 构建和运行

  • 由于这是一个简单示例,没有使用打包工具。如果要在实际项目中,需要使用 Webpack 等工具对代码进行打包,生成 client - bundle.js 文件。
  • 运行 node src/server.js,打开浏览器访问 http://localhost:3000,可以看到渲染好的 React 页面。

使用 Next.js 进行 SSR 开发

  1. Next.js 简介
    • Next.js 是一个基于 React 的轻量级前端框架,它为 React 应用提供了服务器端渲染、静态站点生成等功能。Next.js 具有简洁的 API 和良好的开发体验,使得 SSR 开发变得更加容易。
  2. 项目初始化
    • 使用 npx create - next - app 命令快速创建一个 Next.js 项目。例如,运行 npx create - next - app next - ssr - demo,这会创建一个名为 next - ssr - demo 的 Next.js 项目。
  3. 项目结构
    • pages 目录:Next.js 使用文件系统路由,在 pages 目录下的每个文件都会自动生成对应的路由。例如,pages/index.js 对应根路由 '/'
    • public 目录:用于存放静态文件,如图片、字体等。
    • styles 目录:通常用于存放全局样式文件。
  4. 编写页面组件
    • pages/index.js 文件中编写以下代码:
import React from'react';

const HomePage = () => {
    return (
        <div>
            <h1>Next.js SSR Example</h1>
        </div>
    );
};

export default HomePage;
  1. 运行项目
    • 在项目目录下运行 npm run dev,Next.js 会启动一个开发服务器,默认监听 http://localhost:3000。打开浏览器访问该地址,即可看到渲染好的页面。
  2. 数据获取
    • Next.js 提供了 getStaticPropsgetServerSideProps 两个方法来进行数据获取。
    • getStaticProps:用于在构建时获取数据。例如,在 pages/post/[id].js 文件中(这里 [id] 是动态路由参数):
import React from'react';
import { getAllPosts } from '../lib/api';

const PostPage = ({ post }) => {
    return (
        <div>
            <h1>{post.title}</h1>
            <p>{post.content}</p>
        </div>
    );
};

export async function getStaticProps(context) {
    const id = context.params.id;
    const post = await getAllPosts(id);
    return {
        props: {
            post
        },
        revalidate: 60 // 可选,用于设置数据重新验证时间(秒)
    };
}

export async function getStaticPaths() {
    const posts = await getAllPosts();
    const paths = posts.map(post => ({
        params: { id: post.id.toString() }
    }));
    return { paths, fallback: false };
}

export default PostPage;
  • getServerSideProps:用于在每次请求时获取数据。例如:
import React from'react';
import { getPost } from '../lib/api';

const PostPage = ({ post }) => {
    return (
        <div>
            <h1>{post.title}</h1>
            <p>{post.content}</p>
        </div>
    );
};

export async function getServerSideProps(context) {
    const id = context.params.id;
    const post = await getPost(id);
    return {
        props: {
            post
        }
    };
}

export default PostPage;
  1. 样式处理
    • Next.js 支持多种样式处理方案,如 CSS Modules、Sass 等。以 CSS Modules 为例,在 pages/index.module.css 文件中编写样式:
.title {
    color: blue;
}

然后在 pages/index.js 中引入并使用:

import React from'react';
import styles from './index.module.css';

const HomePage = () => {
    return (
        <div>
            <h1 className={styles.title}>Next.js SSR Example</h1>
        </div>
    );
};

export default HomePage;

SSR 的挑战与解决方案

  1. 性能问题
    • 挑战:服务器端渲染需要在服务器端执行大量的计算,如 React 组件的渲染等,可能会导致服务器负载过高,影响性能。
    • 解决方案
      • 缓存:可以对渲染结果进行缓存,对于相同的请求,直接返回缓存的渲染结果,减少重复计算。例如,使用 Redis 等缓存工具。
      • 优化代码:对 React 组件进行性能优化,如使用 React.memo 来优化函数式组件,避免不必要的渲染。
  2. 状态管理
    • 挑战:在 SSR 应用中,服务器端和客户端的状态管理需要保持一致,否则可能会出现页面闪烁等问题。
    • 解决方案
      • 使用状态管理库:如 Redux、MobX 等。在服务器端和客户端共享状态管理逻辑,通过在服务器端渲染时初始化状态,然后在客户端激活时使用相同的状态。
      • 数据序列化与反序列化:将服务器端的状态序列化为 JSON 格式,通过 HTML 页面传递给客户端,客户端再反序列化并使用该状态。
  3. 资源管理
    • 挑战:在 SSR 中,需要管理服务器端和客户端的资源,如 JavaScript 文件、CSS 文件等,确保正确加载和使用。
    • 解决方案
      • 使用打包工具:如 Webpack,通过配置正确的加载器和插件,将客户端和服务器端代码进行分离打包,确保资源的正确加载。
      • 代码分割:对 JavaScript 代码进行分割,只加载当前页面所需的代码,减少初始加载时间。

SSR 在实际项目中的应用场景

  1. 内容型网站:对于新闻网站、博客等内容型网站,SSR 可以快速将文章内容呈现给用户,提高首屏加载速度,同时有利于搜索引擎优化,吸引更多流量。例如,一些知名的新闻媒体网站采用 SSR 技术,使得用户能够快速获取新闻内容。
  2. 电子商务网站:在电商网站中,产品详情页等页面使用 SSR 可以让用户更快地看到产品信息,提升购物体验。同时,对于一些促销活动页面,快速的首屏加载可以吸引更多用户参与活动。
  3. 企业级应用:企业内部的管理系统等应用,可能存在部分用户使用低性能设备或者网络环境较差的情况。SSR 可以减少客户端渲染压力,确保这些用户能够正常使用应用。例如,企业的办公自动化系统采用 SSR 技术,提高了系统的可访问性。

通过以上对 Node.js 服务器端渲染 SSR 技术的介绍,包括其原理、实现方式、相关框架以及面临的挑战和应用场景等方面,希望读者对 SSR 技术有更深入的理解,并能够在实际项目中合理运用 SSR 技术,提升应用的性能和用户体验。